maws 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/bin/maws +10 -0
  2. data/lib/maws/chunk_source.rb +41 -0
  3. data/lib/maws/command.rb +62 -0
  4. data/lib/maws/command_loader.rb +28 -0
  5. data/lib/maws/command_options_parser.rb +37 -0
  6. data/lib/maws/commands/configure.rb +287 -0
  7. data/lib/maws/commands/console.rb +38 -0
  8. data/lib/maws/commands/create.rb +25 -0
  9. data/lib/maws/commands/describe.rb +15 -0
  10. data/lib/maws/commands/destroy.rb +11 -0
  11. data/lib/maws/commands/elb-add.rb +17 -0
  12. data/lib/maws/commands/elb-describe.rb +23 -0
  13. data/lib/maws/commands/elb-disable-zones.rb +17 -0
  14. data/lib/maws/commands/elb-enable-zones.rb +18 -0
  15. data/lib/maws/commands/elb-remove.rb +16 -0
  16. data/lib/maws/commands/set-prefix.rb +24 -0
  17. data/lib/maws/commands/set-security-groups.rb +442 -0
  18. data/lib/maws/commands/start.rb +11 -0
  19. data/lib/maws/commands/status.rb +25 -0
  20. data/lib/maws/commands/stop.rb +11 -0
  21. data/lib/maws/commands/teardown.rb +11 -0
  22. data/lib/maws/commands/volumes-cleanup.rb +22 -0
  23. data/lib/maws/commands/volumes-status.rb +43 -0
  24. data/lib/maws/commands/wait.rb +61 -0
  25. data/lib/maws/connection.rb +121 -0
  26. data/lib/maws/core_ext/object.rb +5 -0
  27. data/lib/maws/description/ebs.rb +40 -0
  28. data/lib/maws/description/ec2.rb +72 -0
  29. data/lib/maws/description/elb.rb +52 -0
  30. data/lib/maws/description/rds.rb +47 -0
  31. data/lib/maws/description.rb +78 -0
  32. data/lib/maws/instance/ebs.rb +45 -0
  33. data/lib/maws/instance/ec2.rb +144 -0
  34. data/lib/maws/instance/elb.rb +92 -0
  35. data/lib/maws/instance/rds.rb +84 -0
  36. data/lib/maws/instance.rb +167 -0
  37. data/lib/maws/instance_collection.rb +98 -0
  38. data/lib/maws/instance_display.rb +84 -0
  39. data/lib/maws/instance_matcher.rb +27 -0
  40. data/lib/maws/loader.rb +173 -0
  41. data/lib/maws/logger.rb +66 -0
  42. data/lib/maws/mash.rb +9 -0
  43. data/lib/maws/maws.rb +102 -0
  44. data/lib/maws/profile_loader.rb +92 -0
  45. data/lib/maws/specification.rb +127 -0
  46. data/lib/maws/ssh.rb +7 -0
  47. data/lib/maws/trollop.rb +782 -0
  48. data/lib/maws/volumes_command.rb +29 -0
  49. data/lib/maws.rb +25 -0
  50. 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
data/lib/maws/ssh.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'net/ssh'
2
+
3
+ class Net::SSH::Authentication::KeyManager
4
+ def use_agent?
5
+ false
6
+ end
7
+ end