maws 0.8.0
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/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
|