zzdeploy 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|