maws 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/maws +10 -0
- data/lib/maws/chunk_source.rb +41 -0
- data/lib/maws/command.rb +62 -0
- data/lib/maws/command_loader.rb +28 -0
- data/lib/maws/command_options_parser.rb +37 -0
- data/lib/maws/commands/configure.rb +287 -0
- data/lib/maws/commands/console.rb +38 -0
- data/lib/maws/commands/create.rb +25 -0
- data/lib/maws/commands/describe.rb +15 -0
- data/lib/maws/commands/destroy.rb +11 -0
- data/lib/maws/commands/elb-add.rb +17 -0
- data/lib/maws/commands/elb-describe.rb +23 -0
- data/lib/maws/commands/elb-disable-zones.rb +17 -0
- data/lib/maws/commands/elb-enable-zones.rb +18 -0
- data/lib/maws/commands/elb-remove.rb +16 -0
- data/lib/maws/commands/set-prefix.rb +24 -0
- data/lib/maws/commands/set-security-groups.rb +442 -0
- data/lib/maws/commands/start.rb +11 -0
- data/lib/maws/commands/status.rb +25 -0
- data/lib/maws/commands/stop.rb +11 -0
- data/lib/maws/commands/teardown.rb +11 -0
- data/lib/maws/commands/volumes-cleanup.rb +22 -0
- data/lib/maws/commands/volumes-status.rb +43 -0
- data/lib/maws/commands/wait.rb +61 -0
- data/lib/maws/connection.rb +121 -0
- data/lib/maws/core_ext/object.rb +5 -0
- data/lib/maws/description/ebs.rb +40 -0
- data/lib/maws/description/ec2.rb +72 -0
- data/lib/maws/description/elb.rb +52 -0
- data/lib/maws/description/rds.rb +47 -0
- data/lib/maws/description.rb +78 -0
- data/lib/maws/instance/ebs.rb +45 -0
- data/lib/maws/instance/ec2.rb +144 -0
- data/lib/maws/instance/elb.rb +92 -0
- data/lib/maws/instance/rds.rb +84 -0
- data/lib/maws/instance.rb +167 -0
- data/lib/maws/instance_collection.rb +98 -0
- data/lib/maws/instance_display.rb +84 -0
- data/lib/maws/instance_matcher.rb +27 -0
- data/lib/maws/loader.rb +173 -0
- data/lib/maws/logger.rb +66 -0
- data/lib/maws/mash.rb +9 -0
- data/lib/maws/maws.rb +102 -0
- data/lib/maws/profile_loader.rb +92 -0
- data/lib/maws/specification.rb +127 -0
- data/lib/maws/ssh.rb +7 -0
- data/lib/maws/trollop.rb +782 -0
- data/lib/maws/volumes_command.rb +29 -0
- data/lib/maws.rb +25 -0
- metadata +115 -0
data/lib/maws/maws.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'maws/instance_collection'
|
2
|
+
require 'maws/specification'
|
3
|
+
require 'maws/connection'
|
4
|
+
|
5
|
+
|
6
|
+
class Maws
|
7
|
+
attr_reader :instances, :config, :command, :connection, :descriptions
|
8
|
+
|
9
|
+
def initialize(config, command)
|
10
|
+
@config = config
|
11
|
+
@command = command
|
12
|
+
|
13
|
+
@connection = Connection.new(@config)
|
14
|
+
|
15
|
+
@instances = InstanceCollection.new
|
16
|
+
@specification = Specification.new(@config, @config.command_line.selection || "")
|
17
|
+
end
|
18
|
+
|
19
|
+
def services
|
20
|
+
@specification.services
|
21
|
+
end
|
22
|
+
|
23
|
+
def specified_roles
|
24
|
+
@specification.roles
|
25
|
+
end
|
26
|
+
|
27
|
+
def specified_zones
|
28
|
+
@specification.zones
|
29
|
+
end
|
30
|
+
|
31
|
+
def run!
|
32
|
+
connect
|
33
|
+
info "\n"
|
34
|
+
info "REGION: #{@config.region}"
|
35
|
+
info "AVAILABLE ZONES: #{@connection.available_zones.join(', ')}"
|
36
|
+
build_instances
|
37
|
+
|
38
|
+
info "\n"
|
39
|
+
info "QUERYING SERVICES: #{instances.services.join(', ')}"
|
40
|
+
info "TOTAL #{@config.profile.name.upcase} ON AWS: #{instances.aws.count} "
|
41
|
+
info "TOTAL SELECTED: #{instances.specified.count}"
|
42
|
+
info "TOTAL SELECTED ON AWS: #{instances.specified.aws.count}"
|
43
|
+
|
44
|
+
@command.run!
|
45
|
+
end
|
46
|
+
|
47
|
+
def connect(services = nil)
|
48
|
+
services ||= @specification.services
|
49
|
+
@connection.connect(services)
|
50
|
+
|
51
|
+
@config.available_zones = @connection.available_zones
|
52
|
+
@config.specified_zones = @specification.zones
|
53
|
+
end
|
54
|
+
|
55
|
+
def resync_instances
|
56
|
+
@descriptions = @connection.descriptions(services)
|
57
|
+
@descriptions.each {|description|
|
58
|
+
instance = @instances.matching(:aws_id => description.aws_id).first
|
59
|
+
instance.description = description if instance
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def build_instances
|
64
|
+
@descriptions = @connection.descriptions(services)
|
65
|
+
|
66
|
+
create_from_descriptions
|
67
|
+
create_from_specification
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_from_descriptions
|
71
|
+
@descriptions.map { |description|
|
72
|
+
instance = description.create_instance(self, @config)
|
73
|
+
@instances.add(instance)
|
74
|
+
instance.groups << "aws"
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_from_specification
|
79
|
+
@specification.existing_instances = @instances.matching(:groups => 'aws')
|
80
|
+
|
81
|
+
prefix = @config.prefix
|
82
|
+
@specification.role_indexes.each {|role_name, indexes|
|
83
|
+
@specification.zones_for_role(role_name).each {|zone|
|
84
|
+
indexes.each do |index|
|
85
|
+
instance = find_or_create_specified(prefix, zone, role_name, index)
|
86
|
+
instance.groups << "specified"
|
87
|
+
end
|
88
|
+
}
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def find_or_create_specified(prefix, zone, role_name, index)
|
93
|
+
found = @instances.matching({:zone => zone, :role => role_name, :index => index, :prefix => prefix})
|
94
|
+
if !found.empty?
|
95
|
+
found.first
|
96
|
+
else
|
97
|
+
instance = Instance.create(self, @config, prefix, zone, role_name, index)
|
98
|
+
@instances.add(instance)
|
99
|
+
instance
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class ProfileLoader
|
2
|
+
RESERVED_ROLE_NAMES = %w(roles lanes name zones aliases settings security_rules)
|
3
|
+
|
4
|
+
def initialize(config)
|
5
|
+
@config = config
|
6
|
+
end
|
7
|
+
|
8
|
+
def load
|
9
|
+
# assumes profile file is known to exist already
|
10
|
+
load_profile
|
11
|
+
load_roles
|
12
|
+
load_security_rules
|
13
|
+
|
14
|
+
exit_on_missing_roles
|
15
|
+
create_combined
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def load_profile
|
21
|
+
profile_name = @config.command_line.profile_name
|
22
|
+
profile_path = @config.config.available_profiles[profile_name]
|
23
|
+
|
24
|
+
@config.profile = mash(YAML.load_file(profile_path))
|
25
|
+
@config.profile.name = profile_name
|
26
|
+
|
27
|
+
@config.available_roles = @config.profile.keys - RESERVED_ROLE_NAMES
|
28
|
+
sort_available_roles_by_appearance_in_profile(profile_path)
|
29
|
+
end
|
30
|
+
|
31
|
+
def sort_available_roles_by_appearance_in_profile(profile_path)
|
32
|
+
# hacky, but does the job
|
33
|
+
profile_text = File.read(profile_path)
|
34
|
+
@config.available_roles = @config.available_roles.sort_by {|role_name| profile_text.index(role_name+":")}
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_roles
|
38
|
+
roles_file_name = @config.profile.roles
|
39
|
+
roles_file_path = File.join(@config.config.paths.roles, roles_file_name) + ".yml"
|
40
|
+
Loader.config_file_must_exist!('roles file', roles_file_path)
|
41
|
+
|
42
|
+
@config.roles = mash(YAML.load_file(roles_file_path))
|
43
|
+
@config.roles.name = roles_file_name
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_security_rules
|
47
|
+
security_rules_file_name = @config.profile.security_rules
|
48
|
+
|
49
|
+
if security_rules_file_name.blank?
|
50
|
+
else
|
51
|
+
security_rules_file_path = File.join(@config.config.paths.security_rules, security_rules_file_name) + ".yml"
|
52
|
+
Loader.config_file_must_exist!('security rules file', security_rules_file_path)
|
53
|
+
|
54
|
+
@config.security_rules = mash(YAML.load_file(security_rules_file_path))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def exit_on_missing_roles
|
59
|
+
profile_config_role_names = @config.profile.keys - RESERVED_ROLE_NAMES
|
60
|
+
roles_config_role_names = @config.roles.keys - RESERVED_ROLE_NAMES
|
61
|
+
|
62
|
+
unknown_roles_in_profile_config = profile_config_role_names - roles_config_role_names
|
63
|
+
|
64
|
+
unless unknown_roles_in_profile_config.empty?
|
65
|
+
error "Undefined roles [%s] in profile '%s'" % [unknown_roles_in_profile_config.join(', '), @config.profile.name]
|
66
|
+
exit(1)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_combined
|
71
|
+
@config.combined = mash
|
72
|
+
# combine roles
|
73
|
+
@config.available_roles.each {|role_name|
|
74
|
+
@config.combined[role_name] = @config.roles[role_name].deep_merge(@config.profile[role_name])
|
75
|
+
|
76
|
+
# now combine configurations - these are arrays that need to be unioned
|
77
|
+
@config.combined[role_name].configurations = []
|
78
|
+
# first collect all configurations with the same name for this role
|
79
|
+
grouped_configurations = ((@config.roles[role_name].configurations || []) +
|
80
|
+
(@config.profile[role_name].configurations || [])).
|
81
|
+
group_by {|c| c.name }
|
82
|
+
# now merge all for the same name
|
83
|
+
grouped_configurations.each {|name, configurations|
|
84
|
+
merged_configuration = configurations.inject(configurations.first) { |merged, configuration| merged.deep_merge(configuration)}
|
85
|
+
@config.combined[role_name].configurations << merged_configuration
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
89
|
+
# combine settings
|
90
|
+
@config.combined.settings = @config.roles.settings.deep_merge(@config.profile.settings)
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
class Specification
|
2
|
+
LARGEST_INDEX = 999
|
3
|
+
|
4
|
+
attr_accessor :existing_instances
|
5
|
+
|
6
|
+
def initialize(config, spec)
|
7
|
+
@config = config
|
8
|
+
@spec = spec
|
9
|
+
end
|
10
|
+
|
11
|
+
# [:ec2, :rds, :elb]
|
12
|
+
def services
|
13
|
+
roles.map { |role_name|
|
14
|
+
@config.roles[role_name].service
|
15
|
+
}.compact.uniq.map{|service| service.to_sym}
|
16
|
+
end
|
17
|
+
|
18
|
+
# ['a', 'b', 'd']
|
19
|
+
def zones
|
20
|
+
found = @config.available_zones.find_all {|az|
|
21
|
+
@spec.match(%r{\b#{az}\b})
|
22
|
+
}
|
23
|
+
found.empty? ? @config.available_zones : found
|
24
|
+
end
|
25
|
+
|
26
|
+
def zones_for_role(role)
|
27
|
+
if @config.combined[role].scope != 'zone'
|
28
|
+
[nil]
|
29
|
+
else
|
30
|
+
zones
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# {'app' => [1,2,3,4], 'web' => [3], 'db' => [4,5,6,7,8,9,10,11,12]}
|
35
|
+
def role_indexes
|
36
|
+
indexes = {}
|
37
|
+
roles.each {|role_name|
|
38
|
+
indexes[role_name] = indexes_range_for_role(role_name)
|
39
|
+
}
|
40
|
+
|
41
|
+
indexes.delete_if {|role, index_range| index_range.nil?}
|
42
|
+
indexes.each {|role, index_range| indexes[role] = resolve_index_range(role, index_range)}
|
43
|
+
|
44
|
+
indexes
|
45
|
+
end
|
46
|
+
|
47
|
+
# ('app', [nil, *]) => [1,2,3,4,5,6,7]
|
48
|
+
def resolve_index_range(role, range)
|
49
|
+
lower = range[0]
|
50
|
+
upper = range[1]
|
51
|
+
|
52
|
+
lower = 1 if lower.nil?
|
53
|
+
|
54
|
+
# replace upper
|
55
|
+
if upper == '*'
|
56
|
+
# max existing index
|
57
|
+
max_existing = @existing_instances.matching(:role => role).map {|i| i.index}.max.to_i
|
58
|
+
max_profile = @config.profile[role].count
|
59
|
+
|
60
|
+
upper = [max_profile, max_existing].max
|
61
|
+
end
|
62
|
+
|
63
|
+
if upper.nil?
|
64
|
+
# max index in profile
|
65
|
+
upper = @config.profile[role].count
|
66
|
+
end
|
67
|
+
|
68
|
+
lower.upto(upper).to_a
|
69
|
+
end
|
70
|
+
|
71
|
+
def indexes_range_for_role(role)
|
72
|
+
# 'app' => [nil, nil, nil] => [nil, nil]
|
73
|
+
# 'app-' => [nil, nil, nil] => [nil, nil]
|
74
|
+
# 'app-*' => ["*", nil, nil] => [nil, '*']
|
75
|
+
# 'app-3' => ["3", nil, nil] => [3,3]
|
76
|
+
# 'app-3-' => ["3", '-', nil] => [3, nil]
|
77
|
+
# 'app-3-*' => ["3", '-', "*"] => [3, '*']
|
78
|
+
# 'app-1-3' => ["1", '-', "3"] => [1, 3]
|
79
|
+
|
80
|
+
# '*' => [nil, '*']
|
81
|
+
# '' => [nil, nil]
|
82
|
+
|
83
|
+
|
84
|
+
md = @spec.match(%r{\b#{role}(?=\b)-?(?![^\*\d\s])(\d+|\*)?(-)?(\d+|\*)?})
|
85
|
+
|
86
|
+
# no match, either badly formed specification
|
87
|
+
# or '*' or '' (star and blank) specs
|
88
|
+
if md.nil?
|
89
|
+
anyzone = zones.join('')
|
90
|
+
index = if @spec.match(%r{^[#{anyzone}\s]*\*[#{anyzone}\s]*$}) # single '*' with zones
|
91
|
+
[nil, '*']
|
92
|
+
elsif @spec.match(%r{^[#{anyzone}\s]*$}) # blank
|
93
|
+
[nil, nil]
|
94
|
+
else
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
return index
|
98
|
+
end
|
99
|
+
|
100
|
+
lower, separator, upper = md[1], md[2], md[3]
|
101
|
+
|
102
|
+
return nil unless lower.nil? || lower == '*' || lower =~ /^\d+$/
|
103
|
+
return nil unless upper.nil? || upper == '*' || upper =~ /^\d+$/
|
104
|
+
|
105
|
+
|
106
|
+
|
107
|
+
# convert to integers
|
108
|
+
lower = lower.to_i unless lower == '*' or lower.nil?
|
109
|
+
upper = upper.to_i unless upper == '*' or upper.nil?
|
110
|
+
|
111
|
+
# single star is upper bound, not lower bound
|
112
|
+
lower, upper = upper, lower if lower == '*'
|
113
|
+
|
114
|
+
# lower digit without separator and without upper digit means single index
|
115
|
+
if lower and separator.nil? and upper.nil?
|
116
|
+
upper = lower
|
117
|
+
end
|
118
|
+
|
119
|
+
[lower, upper]
|
120
|
+
end
|
121
|
+
|
122
|
+
def roles
|
123
|
+
roles = @config.available_roles.find_all { |role_name| @spec.include?(role_name) }
|
124
|
+
roles = @config.available_roles if roles.blank? # use all if none are specified
|
125
|
+
roles
|
126
|
+
end
|
127
|
+
end
|