zzsharedlib 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1 @@
1
+ Copyright (c) 2011 ZangZing, LLC
data/README.rdoc ADDED
@@ -0,0 +1,10 @@
1
+ = zz
2
+
3
+ Deploy tool for ZangZing and Amazon instances
4
+
5
+ == Installation
6
+ $ gem install zzdeploy
7
+
8
+ == Copyright
9
+
10
+ Copyright (c) 2011 ZangZing, LLC. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'spec/rake/spectask'
2
+ require 'rake/rdoctask'
3
+
4
+ $LOAD_PATH.unshift(File.expand_path('../lib', __FILE__))
5
+
6
+ Spec::Rake::SpecTask.new(:spec) do |spec|
7
+ spec.libs << 'lib' << 'spec'
8
+ spec.spec_files = FileList['spec/**/*_spec.rb']
9
+ end
10
+
11
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
12
+ spec.libs << 'lib' << 'spec'
13
+ spec.pattern = 'spec/**/*_spec.rb'
14
+ spec.rcov = true
15
+ end
16
+
17
+ Rake::RDocTask.new do |rdoc|
18
+ require 'zz_deploy'
19
+ rdoc.rdoc_dir = 'rdoc'
20
+ rdoc.title = "zzdeploy #{ZZDeploy::VERSION}"
21
+ rdoc.rdoc_files.include('README*')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ task :default => :spec
@@ -0,0 +1,11 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+ require 'rubygems'
3
+
4
+ require 'right_aws'
5
+ require 'sdb/active_sdb'
6
+
7
+ require 'zzsharedlib/utilities'
8
+ require 'zzsharedlib/monkey_patches'
9
+ require 'zzsharedlib/amazon'
10
+ require 'zzsharedlib/options'
11
+ require 'zzsharedlib/deploy_group_simple_db'
@@ -0,0 +1,207 @@
1
+ require 'logger'
2
+
3
+ # this class manages global stuff related to the amazon connection
4
+ module ZZSharedLib
5
+
6
+ class Amazon
7
+ attr_reader :ec2, :elb
8
+
9
+ def self.ec2
10
+ @@ec2 ||= RightAws::Ec2.new(access_key, secret_key, :endpoint_url => 'https://ec2.us-east-1.amazonaws.com/', :logger => Amazon.logger)
11
+ end
12
+
13
+ def self.elb
14
+ @@elb ||= RightAws::ElbInterface.new(access_key, secret_key, :logger => Amazon.logger)
15
+ end
16
+
17
+ def self.secret_key
18
+ Options.get(:secret_key) || ENV['AWS_SECRET_ACCESS_KEY']
19
+ end
20
+
21
+ def self.access_key
22
+ Options.get(:access_key) || ENV['AWS_ACCESS_KEY_ID']
23
+ end
24
+
25
+ def self.log_level
26
+ Options.get(:log_level) || Logger::Severity::WARN
27
+ end
28
+
29
+ def self.make_logger
30
+ # need to pass in a logdev for this to work...
31
+ logger = Logger.new(STDOUT)
32
+ logger.level = log_level
33
+ logger
34
+ end
35
+
36
+ def self.logger
37
+ @@logger ||= make_logger
38
+ end
39
+
40
+ def initialize
41
+ connection = RightAws::ActiveSdb.establish_connection(Amazon.access_key, Amazon.secret_key, :logger => Amazon.logger)
42
+ @ec2 = Amazon.ec2
43
+ @elb = Amazon.elb
44
+ @force_tags = true
45
+ end
46
+
47
+ # get the deploy group object from the simple db
48
+ def find_deploy_group(group_name)
49
+ # first see if already exists
50
+ deploy_group = DeployGroupSimpleDB.find_by_zz_object_type_and_group(DeployGroupSimpleDB.object_type, group_name, :auto_load => true)
51
+
52
+ if deploy_group.nil? || deploy_group[:group] != group_name
53
+ raise "Deploy group not found. Make sure you specified the correct deploy group name."
54
+ end
55
+
56
+ deploy_group
57
+ end
58
+
59
+ # call this to ensure the tags come from amazon next time
60
+ def flush_tags
61
+ @force_tags = true
62
+ end
63
+
64
+ # does a describe and returns the map, if we already have it and not doing force
65
+ # return what we already have
66
+ # we filter out any terminated instances
67
+ def describe_tags
68
+ return @describe_tags if @force_tag == false && !@describe_tags.nil?
69
+ @force_tags = false # use from cache next time unless somebody resets flag
70
+ # grab it all and filter later
71
+ all_tags = ec2.describe_tags
72
+
73
+ # filter out any terminated instances
74
+ instances = ec2.describe_instances
75
+ ignore_instances = Set.new
76
+ instances.each do |instance|
77
+ if instance[:aws_state] != 'running'
78
+ ignore_instances << instance[:aws_instance_id]
79
+ end
80
+ end
81
+ filtered_tags = []
82
+ all_tags.each do |tag|
83
+ resource_id = tag[:resource_id]
84
+ filtered_tags << tag unless ignore_instances.include?(resource_id)
85
+ end
86
+
87
+ return @describe_tags = filtered_tags
88
+ end
89
+
90
+ # return all the tags that match a given resource id
91
+ def tags_for_resource(id)
92
+ tags = []
93
+ id = id.to_s
94
+ describe_tags.each do |tag|
95
+ tags << tag if id == tag[:resource_id]
96
+ end
97
+
98
+ return tags
99
+ end
100
+
101
+ # flatten the key/values into a top level hash
102
+ # assumes all tags are for the same resource id
103
+ def flat_tags_for_resource(id)
104
+ tags = tags_for_resource(id)
105
+ flat = {}
106
+ tags.each do |tag|
107
+ key = tag[:key]
108
+ value = tag[:value]
109
+ flat[key.to_sym] = value
110
+ end
111
+
112
+ return flat
113
+ end
114
+
115
+ # find an exact match for a given resource type, key, and value
116
+ # returns a list of the resource_ids that matched
117
+ def find_typed_resource(type, key, value)
118
+ ids = []
119
+ type = type.to_s
120
+ key = key.to_s
121
+ value = value.to_s
122
+ describe_tags.each do |tag|
123
+ if type == tag[:resource_type] && key == tag[:key] && value == tag[:value]
124
+ ids << tag[:resource_id]
125
+ end
126
+ end
127
+
128
+ return ids
129
+ end
130
+
131
+ # finds instances within a group/app by a role
132
+ def find_by_role(group, role)
133
+ match_role = find_typed_resource("instance", :role, role)
134
+ match_group = find_typed_resource("instance", :group, group)
135
+ # find the ones that match all three
136
+ match = match_role & match_group
137
+ end
138
+
139
+ # find with filters and sort by Name, returns
140
+ # array of maps with
141
+ # [{:resource_id => inst_id, :Name => "Instance Name"},...]
142
+ #
143
+ def find_and_sort_named_instances(group = nil, role = nil, ready_only = true)
144
+ instances = {}
145
+ describe_tags.each do |tag|
146
+ if "instance" == tag[:resource_type]
147
+ resource_id = tag[:resource_id]
148
+ inst = instances[resource_id]
149
+ if inst.nil?
150
+ inst = { :resource_id => resource_id }
151
+ instances[resource_id] = inst
152
+ end
153
+ key = tag[:key].to_sym
154
+ value = tag[:value]
155
+ inst[key] = value
156
+ end
157
+ end
158
+
159
+ # ok, we've collected the data now we need to filter it
160
+ filtered_instances = []
161
+ need_describe = []
162
+ instances.each_value do |inst|
163
+ next if inst[:group].nil? ||(group.nil? == false && inst[:group] != group.to_s)
164
+ next if inst[:role].nil? || (role.nil? == false && inst[:role] != role.to_s)
165
+ next if ready_only && inst[:state] != "ready"
166
+ filtered_instances << inst
167
+ need_describe << inst[:resource_id]
168
+ end
169
+ filtered_instances.sort! { |a,b| a[:Name] <=> b[:Name] }
170
+
171
+ # last step is to describe the instances we care about to get
172
+ # more info and add that to each instance returned
173
+ az_instances = ec2.describe_instances(need_describe)
174
+ az_hash = {}
175
+ az_instances.each do |instance|
176
+ key = instance[:aws_instance_id]
177
+ az_hash[key] = instance
178
+ end
179
+ # now update the info on filtered instances
180
+ filtered_instances.each do |instance|
181
+ inst_id = instance[:resource_id]
182
+ detail = az_hash[inst_id]
183
+ instance[:public_hostname] = detail[:dns_name]
184
+ instance[:local_hostname] = detail[:private_dns_name]
185
+ end
186
+ return filtered_instances
187
+ end
188
+
189
+ # similar to find_and_sort_named_instances but
190
+ # returns a hash with the instance id as the key
191
+ # for each element
192
+ #
193
+ # {:instance_id => { :Name => "Instance Name", :role => role},...}
194
+ #
195
+ def find_named_instances(group = nil, role = nil, ready_only = true)
196
+ remapped = {}
197
+ instances = find_and_sort_named_instances(group, role)
198
+ instances.each do |instance|
199
+ key = instance[:resource_id].to_sym
200
+ remapped[key] = instance
201
+ end
202
+ return remapped
203
+ end
204
+
205
+ end
206
+
207
+ end
@@ -0,0 +1,33 @@
1
+ module ZZSharedLib
2
+ class DeployGroupSimpleDB < RightAws::ActiveSdb::Base
3
+ set_domain_name "deploy"
4
+
5
+ columns do
6
+ zz_object_type
7
+ group
8
+ config_json
9
+ recipes_deploy_tag
10
+ app_deploy_tag
11
+ created_at
12
+ updated_at
13
+ end
14
+
15
+ def self.object_type
16
+ @@object_type ||= 'deploy_group_type'.freeze
17
+ end
18
+
19
+ def config
20
+ @config ||= JSON.parse(self.config_json).recursively_symbolize_keys!
21
+ end
22
+
23
+ def config=(object)
24
+ self.config_json = JSON.fast_generate(object)
25
+ end
26
+
27
+ # use strings for time
28
+ def save
29
+ self.updated_at = Time.now.strftime('%Y-%m-%dT%H:%M:%S%z')
30
+ super
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,39 @@
1
+ # serialize the data with padding
2
+ class RightAws::ActiveSdb
3
+ class IntegerSerialization
4
+ class << self
5
+ def serialize(int)
6
+ str = int.to_s
7
+ str = str.rjust(12, '0')
8
+ str
9
+ end
10
+
11
+ def deserialize(string)
12
+ string.to_i
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+
19
+ class Hash
20
+ # A method to recursively symbolize all keys in the Hash class
21
+ def recursively_symbolize_keys!
22
+ self.symbolize_keys!
23
+ self.values.each do |v|
24
+ if v.is_a? Hash
25
+ v.recursively_symbolize_keys!
26
+ elsif v.is_a? Array
27
+ #v.recursively_symbolize_keys!
28
+ end
29
+ end
30
+ self
31
+ end
32
+
33
+ def symbolize_keys!
34
+ keys.each do |key|
35
+ self[(key.to_sym rescue key) || key] = delete(key)
36
+ end
37
+ self
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ module ZZSharedLib
2
+ # tracks the global and per command options but lets you
3
+ # fetch values without regard to which one. The command
4
+ # is checked before the global
5
+ class Options
6
+ def self.global_options=(options)
7
+ @@global_options = options
8
+ end
9
+
10
+ def self.cmd_options=(options)
11
+ @@cmd_options = options
12
+ end
13
+
14
+ def self.cmd_options
15
+ @@cmd_options ||= {}
16
+ end
17
+
18
+ def self.global_options
19
+ @@global_options ||= {}
20
+ end
21
+
22
+ def self.get(option)
23
+ v = cmd_options[option]
24
+ return v if !v.nil?
25
+
26
+ v = global_options[option]
27
+ return v
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,64 @@
1
+ module ZZSharedLib
2
+ class CL
3
+ # run a command line and echo to console
4
+ def self.do_cmd(cmd)
5
+ puts cmd
6
+ Kernel.system(cmd)
7
+ end
8
+
9
+ # same as above but returns the result code
10
+ # 0 is success, anything else is an error code
11
+ def self.do_cmd_result(cmd)
12
+ do_cmd(cmd)
13
+ $?.exitstatus
14
+ end
15
+ end
16
+
17
+ class Utils
18
+ READY = "ready".freeze
19
+ ERROR = "error".freeze
20
+ START = "deploying".freeze
21
+ MAINT = "maint".freeze
22
+ NEVER = "never".freeze
23
+ RESTARTING = "restarting".freeze
24
+ OK_TO_DEPLOY_STATES = [NEVER, READY, ERROR].freeze
25
+
26
+ def initialize(amazon)
27
+ @amazon = amazon
28
+ end
29
+
30
+ # mark the deploy state of all unless already marked
31
+ # as errors so we don't overwrite that state
32
+ def mark_deploy_state(instances, state_tag, state, keep_error_state = false)
33
+ to_tag = []
34
+ instances.each do |instance|
35
+ inst_id = instance[:resource_id]
36
+ if keep_error_state
37
+ tags = @amazon.flat_tags_for_resource(inst_id)
38
+ deploy_tag = tags[state_tag]
39
+ if !deploy_tag.nil? && deploy_tag != ERROR
40
+ to_tag << inst_id
41
+ end
42
+ else
43
+ to_tag << inst_id
44
+ end
45
+ end
46
+ @amazon.ec2.create_tags(to_tag, {state_tag => state })
47
+ @amazon.flush_tags
48
+ end
49
+
50
+ def check_deploy_state(instances, state_tags)
51
+ instances.each do |instance|
52
+ inst_id = instance[:resource_id]
53
+ tags = @amazon.flat_tags_for_resource(inst_id)
54
+ state_tags.each do |state_tag|
55
+ deploy_tag = tags[state_tag]
56
+ if !deploy_tag.nil? && !OK_TO_DEPLOY_STATES.include?(deploy_tag)
57
+ raise "One or more instances is still marked as deploying for tag key: #{state_tag}, value: #{deploy_tag}, we will not deploy again. You can use --force to override"
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zzsharedlib
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Greg Seitz
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-05 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: right_aws
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 27
29
+ segments:
30
+ - 1
31
+ - 3
32
+ - 0
33
+ version: 1.3.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: json
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 15
45
+ segments:
46
+ - 1
47
+ - 4
48
+ - 4
49
+ version: 1.4.4
50
+ - - <=
51
+ - !ruby/object:Gem::Version
52
+ hash: 7
53
+ segments:
54
+ - 1
55
+ - 5
56
+ - 2
57
+ version: 1.5.2
58
+ type: :runtime
59
+ version_requirements: *id002
60
+ description: Useful utility library
61
+ email:
62
+ executables: []
63
+
64
+ extensions: []
65
+
66
+ extra_rdoc_files: []
67
+
68
+ files:
69
+ - lib/zzsharedlib/amazon.rb
70
+ - lib/zzsharedlib/deploy_group_simple_db.rb
71
+ - lib/zzsharedlib/monkey_patches.rb
72
+ - lib/zzsharedlib/options.rb
73
+ - lib/zzsharedlib/utilities.rb
74
+ - lib/zzsharedlib.rb
75
+ - Rakefile
76
+ - LICENSE
77
+ - README.rdoc
78
+ homepage:
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options:
83
+ - --charset=UTF-8
84
+ require_paths:
85
+ - lib
86
+ - lib/zzsharedlib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 31
93
+ segments:
94
+ - 1
95
+ - 8
96
+ version: "1.8"
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ hash: 9
103
+ segments:
104
+ - 1
105
+ - 3
106
+ version: "1.3"
107
+ requirements: []
108
+
109
+ rubyforge_project:
110
+ rubygems_version: 1.8.6
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: ZangZing Utility library
114
+ test_files: []
115
+