zzsharedlib 0.0.4

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 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
+