zzdeploy 0.0.5
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/LICENSE +1 -0
- data/README.rdoc +10 -0
- data/Rakefile +25 -0
- data/bin/zz +14 -0
- data/lib/commands/add_instance.rb +187 -0
- data/lib/commands/build_deploy_config.rb +143 -0
- data/lib/commands/chef_bake.rb +61 -0
- data/lib/commands/chef_upload.rb +62 -0
- data/lib/commands/config_amazon.rb +64 -0
- data/lib/commands/delete_instances.rb +114 -0
- data/lib/commands/deploy_group_create.rb +128 -0
- data/lib/commands/deploy_group_delete.rb +42 -0
- data/lib/commands/deploy_group_list.rb +41 -0
- data/lib/commands/deploy_group_modify.rb +65 -0
- data/lib/commands/deploy_instances.rb +108 -0
- data/lib/commands/list_instances.rb +56 -0
- data/lib/commands/maint_instances.rb +63 -0
- data/lib/commands/meta_options.rb +26 -0
- data/lib/commands/multi_ssh_instance.rb +87 -0
- data/lib/commands/ssh_instance.rb +98 -0
- data/lib/commands.rb +23 -0
- data/lib/info.rb +5 -0
- data/lib/multi_ssh.rb +155 -0
- data/lib/printer.rb +54 -0
- data/lib/zz_deploy.rb +177 -0
- data/spec/spec_helper.rb +5 -0
- metadata +219 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
module Commands
|
2
|
+
class DeployGroupCreate
|
3
|
+
|
4
|
+
# holds the options that were passed
|
5
|
+
# you can set any initial defaults here
|
6
|
+
def options
|
7
|
+
@options ||= {
|
8
|
+
:amazon_elb => ''
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
# required options
|
13
|
+
def required_options
|
14
|
+
@required_options ||= Set.new [
|
15
|
+
:group,
|
16
|
+
:app_name,
|
17
|
+
:rails_env,
|
18
|
+
:vhost,
|
19
|
+
:email_host,
|
20
|
+
:app_git_url,
|
21
|
+
:amazon_security_key,
|
22
|
+
:amazon_security_group,
|
23
|
+
:amazon_image,
|
24
|
+
:database_host,
|
25
|
+
:database_username,
|
26
|
+
:database_password,
|
27
|
+
:database_schema
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
def register(opts, global_options)
|
32
|
+
opts.banner = "Usage: create_deploy_group [options]"
|
33
|
+
opts.description = "Create a deploy group"
|
34
|
+
|
35
|
+
opts.on('-g', "--group name", "Required - Name of this deploy group.") do |v|
|
36
|
+
options[:group] = v
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("--app_name appname", "Required - Name of the application.") do |v|
|
40
|
+
options[:app_name] = v
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("--rails_env rails_env", "Required - The rails environment to use on deploy.") do |v|
|
44
|
+
options[:rails_env] = v
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("--vhost vhost", "Required - The vhost your server will be deployed as.") do |v|
|
48
|
+
options[:vhost] = v
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("--email_host emailhost", "Required - The email host name to use for incomming email processing.") do |v|
|
52
|
+
options[:email_host] = v
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on("--app_git_url app_git_url", "Required - Git URL to fetch code from.") do |v|
|
56
|
+
options[:app_git_url] = v
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on("--extra_json_file extra", "Optional - A file with JSON that has application custom context associated with this deploy group.") do |v|
|
60
|
+
options[:extra_json_file] = v
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on("--zone availability_zone", MetaOptions.availability_zones, "The amazon availability zone - currently only east coast.") do |v|
|
64
|
+
options[:availability_zone] = v
|
65
|
+
end
|
66
|
+
|
67
|
+
opts.on("--amazon_security_key key", "Required - The SSH key name to use, assumes pre-configured on Amazon.") do |v|
|
68
|
+
options[:amazon_security_key] = v
|
69
|
+
end
|
70
|
+
|
71
|
+
opts.on("--amazon_security_group group", "Required - The amazon security group, assumes pre-configured on Amazon.") do |v|
|
72
|
+
options[:amazon_security_group] = v
|
73
|
+
end
|
74
|
+
|
75
|
+
opts.on("--amazon_image ami", "Required - The baseline Amazon image to use, assumes pre-configured on Amazon.") do |v|
|
76
|
+
options[:amazon_image] = v
|
77
|
+
end
|
78
|
+
|
79
|
+
opts.on("--amazon_elb load_balancer", "Optional - The elastic load balancer we operate under.") do |v|
|
80
|
+
options[:amazon_elb] = v
|
81
|
+
end
|
82
|
+
|
83
|
+
opts.on("--database_host database", "Required - The database host name.") do |v|
|
84
|
+
options[:database_host] = v
|
85
|
+
end
|
86
|
+
|
87
|
+
opts.on("--database_username username", "Required - The database user name name.") do |v|
|
88
|
+
options[:database_username] = v
|
89
|
+
end
|
90
|
+
|
91
|
+
opts.on("--database_password password", "Required - The database password.") do |v|
|
92
|
+
options[:database_password] = v
|
93
|
+
end
|
94
|
+
|
95
|
+
opts.on("--database_schema schema", "Required - The database schema name.") do |v|
|
96
|
+
options[:database_schema] = v
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
def run(global_options, amazon)
|
103
|
+
group_name = options[:group]
|
104
|
+
extra_file = options[:extra_json_file]
|
105
|
+
if !extra_file.nil?
|
106
|
+
# they are passing a path to a file containing custom json so add it to the extra field
|
107
|
+
options.delete(:extra_json_file)
|
108
|
+
json = File.open(extra_file, 'r') {|f| f.read }
|
109
|
+
# make sure we can parse into a hash
|
110
|
+
extra = JSON.parse(json)
|
111
|
+
options[:extra] = extra
|
112
|
+
end
|
113
|
+
config_json = JSON.fast_generate(options)
|
114
|
+
|
115
|
+
# first see if already exists
|
116
|
+
deploy_group = ZZSharedLib::DeployGroupSimpleDB.find_by_zz_object_type_and_group(ZZSharedLib::DeployGroupSimpleDB.object_type, group_name, :auto_load => true)
|
117
|
+
|
118
|
+
if !deploy_group.nil?
|
119
|
+
raise "This deploy group already exists. Doing nothing."
|
120
|
+
end
|
121
|
+
|
122
|
+
deploy_group = ZZSharedLib::DeployGroupSimpleDB::create(:zz_object_type => ZZSharedLib::DeployGroupSimpleDB.object_type, :group => group_name,
|
123
|
+
:config_json => config_json, :recipes_deploy_tag => "origin/master", :app_deploy_tag => "master",
|
124
|
+
:created_at => Time.now.strftime('%Y-%m-%dT%H:%M:%S%z'))
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Commands
|
2
|
+
class DeployGroupDelete
|
3
|
+
|
4
|
+
# holds the options that were passed
|
5
|
+
# you can set any initial defaults here
|
6
|
+
def options
|
7
|
+
@options ||= {
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
# required options
|
12
|
+
def required_options
|
13
|
+
@required_options ||= Set.new [
|
14
|
+
:group,
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
18
|
+
def register(opts, global_options)
|
19
|
+
opts.banner = "Usage: delete_deploy_group [options]"
|
20
|
+
opts.description = "Delete a deploy group"
|
21
|
+
|
22
|
+
opts.on('-g', "--group name", "Required - Name of this deploy group.") do |v|
|
23
|
+
options[:group] = v
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def run(global_options, amazon)
|
30
|
+
group_name = options[:group]
|
31
|
+
|
32
|
+
# first see if already exists
|
33
|
+
deploy_group = amazon.find_deploy_group(group_name)
|
34
|
+
|
35
|
+
if deploy_group.nil? || deploy_group[:group] != group_name
|
36
|
+
raise "Deploy group not found. Doing nothing."
|
37
|
+
end
|
38
|
+
|
39
|
+
deploy_group.delete
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Commands
|
2
|
+
class DeployGroupList
|
3
|
+
|
4
|
+
# holds the options that were passed
|
5
|
+
# you can set any initial defaults here
|
6
|
+
def options
|
7
|
+
@options ||= {
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
# required options
|
12
|
+
def required_options
|
13
|
+
@required_options ||= Set.new [
|
14
|
+
]
|
15
|
+
end
|
16
|
+
|
17
|
+
def register(opts, global_options)
|
18
|
+
opts.banner = "Usage: list_deploy_groups [options]"
|
19
|
+
opts.description = "List the deploy groups"
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def run(global_options, amazon)
|
25
|
+
ec2 = amazon.ec2
|
26
|
+
|
27
|
+
# first see if already exists
|
28
|
+
deploy_groups = ZZSharedLib::DeployGroupSimpleDB.find_all_by_zz_object_type(ZZSharedLib::DeployGroupSimpleDB.object_type, :auto_load => true)
|
29
|
+
|
30
|
+
deploy_groups.each do |deploy_group|
|
31
|
+
puts "Name: #{deploy_group[:group]}"
|
32
|
+
puts "Recipes_deploy_tag: #{deploy_group[:recipes_deploy_tag]}"
|
33
|
+
puts "App_deploy_tag: #{deploy_group[:app_deploy_tag]}"
|
34
|
+
puts "Config Json:"
|
35
|
+
pretty = JSON.pretty_generate(deploy_group.config)
|
36
|
+
puts "#{pretty}"
|
37
|
+
puts
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Commands
|
2
|
+
class DeployGroupModify
|
3
|
+
|
4
|
+
# holds the options that were passed
|
5
|
+
# you can set any initial defaults here
|
6
|
+
def options
|
7
|
+
@options ||= {
|
8
|
+
:group,
|
9
|
+
:extra_json_file
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
# required options
|
14
|
+
def required_options
|
15
|
+
@required_options ||= Set.new [
|
16
|
+
:group,
|
17
|
+
:extra_json_file
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
def register(opts, global_options)
|
22
|
+
opts.banner = "Usage: deploy_group_modify [options]"
|
23
|
+
opts.description = "Lets you modify the app extra data by replacing the current extra data with the new data supplied."
|
24
|
+
|
25
|
+
opts.on('-g', "--group name", "Required - Name of this deploy group.") do |v|
|
26
|
+
options[:group] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-e', "--extra_json_file extra", "Required - A file with JSON that has application custom context associated with this deploy group.") do |v|
|
30
|
+
options[:extra_json_file] = v
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
def run(global_options, amazon)
|
37
|
+
ec2 = amazon.ec2
|
38
|
+
|
39
|
+
group_name = options[:group]
|
40
|
+
extra_file = options[:extra_json_file]
|
41
|
+
if !extra_file.nil?
|
42
|
+
# they are passing a path to a file containing custom json so add it to the extra field
|
43
|
+
json = File.open(extra_file, 'r') {|f| f.read }
|
44
|
+
# make sure we can parse into a hash
|
45
|
+
extra = JSON.parse(json)
|
46
|
+
options[:extra] = extra
|
47
|
+
end
|
48
|
+
|
49
|
+
# get the existing deploy group
|
50
|
+
deploy_group = amazon.find_deploy_group(group_name)
|
51
|
+
config = deploy_group.config
|
52
|
+
extra_file = options[:extra_json_file]
|
53
|
+
|
54
|
+
# open up the specified file with the json and parse
|
55
|
+
json = File.open(extra_file, 'r') {|f| f.read }
|
56
|
+
# make sure we can parse into a hash
|
57
|
+
extra = JSON.parse(json)
|
58
|
+
|
59
|
+
# ok, now set or replace any existing extra data
|
60
|
+
config[:extra] = extra
|
61
|
+
deploy_group.config = config
|
62
|
+
deploy_group.save
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Commands
|
2
|
+
class DeployInstances
|
3
|
+
|
4
|
+
# holds the options that were passed
|
5
|
+
# you can set any initial defaults here
|
6
|
+
def options
|
7
|
+
@options ||= {
|
8
|
+
:migrate_command => '',
|
9
|
+
:downtime => false,
|
10
|
+
:no_restart => false
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
# required options
|
15
|
+
def required_options
|
16
|
+
@required_options ||= Set.new [
|
17
|
+
:group,
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
def register(opts, global_options)
|
22
|
+
opts.banner = "Usage: deploy [options]"
|
23
|
+
opts.description = "Deploy the applications for the specified group"
|
24
|
+
|
25
|
+
opts.on('-g', "--group name", "Required - Name of this deploy group.") do |v|
|
26
|
+
options[:group] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-t', "--tag tag", "Required - Git tag to use for pulling chef code.") do |v|
|
30
|
+
options[:tag] = v
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-m', "--migrate [command]", "Migrate command to run. Does not force downtime, use the --downtime option for that.") do |v|
|
34
|
+
options[:migrate_command] = v || 'rake db:migrate'
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-d', "--downtime", "If this flag is set we bring the server down and bring up the maint page during the restart phase.") do |v|
|
38
|
+
options[:downtime] = v
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on("--no_restart", "Set this if you only want the deploy without a restart, useful for creating AMI images.") do |v|
|
42
|
+
options[:no_restart] = v
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.on('-f', "--force", "Force a deploy even if the current status says we are deploying. You should only do this if you are certain the previous deploy is stuck.") do |v|
|
46
|
+
options[:force] = v
|
47
|
+
end
|
48
|
+
|
49
|
+
opts.on('-p', "--print path", "The directory into which we output the data as a file per host.") do |v|
|
50
|
+
options[:result_path] = v
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def run(global_options, amazon)
|
56
|
+
ec2 = amazon.ec2
|
57
|
+
utils = ZZSharedLib::Utils.new(amazon)
|
58
|
+
|
59
|
+
group_name = options[:group]
|
60
|
+
migrate_command = options[:migrate_command]
|
61
|
+
downtime = options[:downtime]
|
62
|
+
app_deploy_tag = options[:tag]
|
63
|
+
no_restart = options[:no_restart]
|
64
|
+
|
65
|
+
# first see if already exists
|
66
|
+
deploy_group = amazon.find_deploy_group(group_name)
|
67
|
+
|
68
|
+
group_config = deploy_group.config
|
69
|
+
gitrepo = group_config[:app_git_url]
|
70
|
+
|
71
|
+
# verify that the chef deploy tag exists
|
72
|
+
full_ref = "refs/heads/#{app_deploy_tag}"
|
73
|
+
cmd = "git ls-remote #{gitrepo} #{full_ref} | egrep #{full_ref}"
|
74
|
+
if ZZSharedLib::CL.do_cmd_result(cmd) != 0
|
75
|
+
# now try to see if it's a tag
|
76
|
+
full_ref = "refs/tags/#{app_deploy_tag}"
|
77
|
+
cmd = "git ls-remote #{gitrepo} #{full_ref} | egrep #{full_ref}"
|
78
|
+
if ZZSharedLib::CL.do_cmd_result(cmd) != 0
|
79
|
+
raise "Could not find the tag or ref: #{app_deploy_tag} in the remote #{gitrepo} repository."
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
deploy_group.app_deploy_tag = app_deploy_tag
|
84
|
+
deploy_group.save
|
85
|
+
deploy_group.reload # save corrupts the in memory state so must reload, kinda lame
|
86
|
+
|
87
|
+
|
88
|
+
instances = amazon.find_and_sort_named_instances(group_name)
|
89
|
+
|
90
|
+
# see if already deploying
|
91
|
+
if !options[:force]
|
92
|
+
# raises an exception if not all in the ready or error state
|
93
|
+
utils.check_deploy_state(instances, [:deploy_chef, :deploy_app])
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
# tag is good, go ahead and deploy to all the machines in the group
|
98
|
+
# this is a two phase operation. The first pushes the code and preps everything
|
99
|
+
# up to the point of a before restart operation. We then call again to
|
100
|
+
# perform the restart. This ensures that all servers were prepped before
|
101
|
+
# we try to restart to minimize the chance of issues.
|
102
|
+
BuildDeployConfig.do_app_deploy(utils, amazon, instances, group_name, deploy_group, migrate_command, downtime, no_restart, options[:result_path])
|
103
|
+
ui = Printer.new(STDOUT, STDERR, STDIN)
|
104
|
+
ui.msg(ui.color("Your app has been successfully deployed - open for business.", :green, :bold, :reverse))
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module Commands
|
4
|
+
class ListInstances
|
5
|
+
|
6
|
+
# holds the options that were passed
|
7
|
+
# you can set any initial defaults here
|
8
|
+
def options
|
9
|
+
@options ||= {
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
# required options
|
14
|
+
def required_options
|
15
|
+
@required_options ||= Set.new [
|
16
|
+
:group
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def register(opts, global_options)
|
21
|
+
opts.banner = "Usage: add [options]"
|
22
|
+
opts.description = "Add a server instance"
|
23
|
+
|
24
|
+
opts.on('-r', "--role role", MetaOptions.roles, "Role to look for.") do |v|
|
25
|
+
options[:role] = v
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on('-g', "--group deploy_group", "Required: Group to look for.") do |v|
|
29
|
+
options[:group] = v
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def run(global_options, amazon)
|
36
|
+
ec2 = amazon.ec2
|
37
|
+
|
38
|
+
instances = amazon.find_and_sort_named_instances(options[:group], options[:role])
|
39
|
+
|
40
|
+
first = true
|
41
|
+
instances.each do |instance|
|
42
|
+
if first
|
43
|
+
s = sprintf("%-40s%-14s%-40s","Name", "Instance", "Public Host")
|
44
|
+
puts s
|
45
|
+
first = false
|
46
|
+
end
|
47
|
+
name = instance[:Name]
|
48
|
+
resource_id = instance[:resource_id]
|
49
|
+
public_host = instance[:public_hostname]
|
50
|
+
s = sprintf("%-40s%-14s%-40s",name, resource_id, public_host)
|
51
|
+
puts s
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Commands
|
2
|
+
class MaintInstances
|
3
|
+
|
4
|
+
# holds the options that were passed
|
5
|
+
# you can set any initial defaults here
|
6
|
+
def options
|
7
|
+
@options ||= {
|
8
|
+
:migrate_command => '',
|
9
|
+
:downtime => false
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
# required options
|
14
|
+
def required_options
|
15
|
+
@required_options ||= Set.new [
|
16
|
+
:group,
|
17
|
+
:maint,
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
def register(opts, global_options)
|
22
|
+
opts.banner = "Usage: maint [options]"
|
23
|
+
opts.description = "Put up or take down the maintenance page."
|
24
|
+
|
25
|
+
opts.on('-g', "--group name", "Required - Name of this deploy group.") do |v|
|
26
|
+
options[:group] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-m', "--[no-]maint", "Required - Use --maint if you want the maint page, --no-maint to remove the maint page.") do |v|
|
30
|
+
options[:maint] = v
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-p', "--print path", "The directory into which we output the data as a file per host.") do |v|
|
34
|
+
options[:result_path] = v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
def run(global_options, amazon)
|
40
|
+
ec2 = amazon.ec2
|
41
|
+
utils = ZZSharedLib::Utils.new(amazon)
|
42
|
+
|
43
|
+
group_name = options[:group]
|
44
|
+
maint = options[:maint]
|
45
|
+
|
46
|
+
# first see if already exists
|
47
|
+
deploy_group = amazon.find_deploy_group(group_name)
|
48
|
+
|
49
|
+
instances = amazon.find_and_sort_named_instances(group_name)
|
50
|
+
|
51
|
+
# see if already deploying
|
52
|
+
if !options[:force]
|
53
|
+
# raises an exception if not all in the ready or error state
|
54
|
+
utils.check_deploy_state(instances, [:deploy_chef, :deploy_app])
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
# put up or take down the maint page
|
59
|
+
BuildDeployConfig.do_maint_deploy(utils, amazon, instances, group_name, deploy_group, maint, options[:result_path])
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# this class defines the valid meta options we allow
|
2
|
+
# such as the role types allowed, apps allowed, environments allowed
|
3
|
+
#
|
4
|
+
module Commands
|
5
|
+
class MetaOptions
|
6
|
+
# the roles that a given server can play
|
7
|
+
# only one role per server is allowed. If we
|
8
|
+
# need custom functionality we define a role that
|
9
|
+
# has the functionality we need.
|
10
|
+
# The meaning of the roles is defined in the chef script
|
11
|
+
# mapping from a role to recipes
|
12
|
+
def self.roles
|
13
|
+
[:app_master, :app, :db, :util, :db_slave, :solo]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.availability_zones
|
17
|
+
["us-east-1a", "us-east-1b", "us-east-1c", "us-east-1d"]
|
18
|
+
end
|
19
|
+
|
20
|
+
# the valid apps we can deploy
|
21
|
+
def self.apps
|
22
|
+
[:photos, :zza, :rollup]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "set"
|
2
|
+
require 'readline'
|
3
|
+
|
4
|
+
module Commands
|
5
|
+
class MultiSSHInstance
|
6
|
+
|
7
|
+
# holds the options that were passed
|
8
|
+
# you can set any initial defaults here
|
9
|
+
def options
|
10
|
+
@options ||= {
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
# required options
|
15
|
+
def required_options
|
16
|
+
@required_options ||= Set.new [
|
17
|
+
:group
|
18
|
+
]
|
19
|
+
end
|
20
|
+
|
21
|
+
def register(opts, global_options)
|
22
|
+
opts.banner = "Usage: ssh [options]"
|
23
|
+
opts.description = "SSH into a server"
|
24
|
+
|
25
|
+
opts.on('-i', "--instances instance1,instance2,etc", Array, "The instance(s) to connect to.") do |v|
|
26
|
+
options[:instances] = v
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on('-r', "--role role", MetaOptions.roles, "Role to look for.") do |v|
|
30
|
+
options[:role] = v
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.on('-g', "--group deploy_group", "Required: Group to look for.") do |v|
|
34
|
+
options[:group] = v
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on('-f', "--file file", "A file that contains the commands to execute.") do |v|
|
38
|
+
options[:file] = v
|
39
|
+
end
|
40
|
+
|
41
|
+
opts.on('-p', "--print path", "The directory into which we output the data as a file per host.") do |v|
|
42
|
+
options[:result_path] = v
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
def run(global_options, amazon)
|
48
|
+
ec2 = amazon.ec2
|
49
|
+
|
50
|
+
file_path = options[:file]
|
51
|
+
if file_path.nil?
|
52
|
+
if ARGV.length != 1
|
53
|
+
raise "Must include the remote command to run. Make sure you quote it so it appears as one argument"
|
54
|
+
end
|
55
|
+
remote_cmd = ARGV[0]
|
56
|
+
else
|
57
|
+
remote_cmd = File.open(file_path, 'r') {|f| f.read }
|
58
|
+
end
|
59
|
+
|
60
|
+
group_name = options[:group]
|
61
|
+
deploy_group = amazon.find_deploy_group(group_name)
|
62
|
+
group_config = deploy_group.config
|
63
|
+
|
64
|
+
user_instances = options[:instances]
|
65
|
+
if user_instances.nil?
|
66
|
+
instances = amazon.find_and_sort_named_instances(options[:group], options[:role])
|
67
|
+
else
|
68
|
+
instances = [] # build the instances wanted here
|
69
|
+
server_instances = amazon.find_and_sort_named_instances(nil, nil, false)
|
70
|
+
server_instances.each do |server_instance|
|
71
|
+
server_instance_id = server_instance[:resource_id]
|
72
|
+
if user_instances.include?(server_instance_id)
|
73
|
+
instances << server_instance
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if instances.empty?
|
79
|
+
raise "No instances matched your search criteria."
|
80
|
+
end
|
81
|
+
|
82
|
+
multi = MultiSSH.new(amazon, group_name, deploy_group)
|
83
|
+
multi.run_instances(instances, remote_cmd)
|
84
|
+
multi.output_tracked_data_to_files("multi_ssh", options[:result_path])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|