zzsharedlib 0.0.4
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/lib/zzsharedlib.rb +11 -0
- data/lib/zzsharedlib/amazon.rb +207 -0
- data/lib/zzsharedlib/deploy_group_simple_db.rb +33 -0
- data/lib/zzsharedlib/monkey_patches.rb +39 -0
- data/lib/zzsharedlib/options.rb +30 -0
- data/lib/zzsharedlib/utilities.rb +64 -0
- metadata +115 -0
data/LICENSE
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Copyright (c) 2011 ZangZing, LLC
|
data/README.rdoc
ADDED
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
|
data/lib/zzsharedlib.rb
ADDED
@@ -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
|
+
|