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