ec2_templater 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,65 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://ec2.ap-southeast-2.amazonaws.com/
6
+ body:
7
+ encoding: UTF-8
8
+ string: Action=TerminateInstances&InstanceId.1=i-e094e03f&Version=2015-10-01
9
+ headers:
10
+ Content-Type:
11
+ - application/x-www-form-urlencoded; charset=utf-8
12
+ Accept-Encoding:
13
+ - ''
14
+ User-Agent:
15
+ - aws-sdk-ruby2/2.2.3 ruby/2.2.3 x86_64-darwin14
16
+ X-Amz-Date:
17
+ - 20151207T062038Z
18
+ Host:
19
+ - ec2.ap-southeast-2.amazonaws.com
20
+ X-Amz-Content-Sha256:
21
+ - 49425530df33c5d2ce5f8e9c477a9fa01bc1a48cba0d205becb046f94685167b
22
+ Authorization:
23
+ - "<AUTHORIZATION>"
24
+ Content-Length:
25
+ - '68'
26
+ Accept:
27
+ - "*/*"
28
+ response:
29
+ status:
30
+ code: 200
31
+ message: OK
32
+ headers:
33
+ Content-Type:
34
+ - text/xml;charset=UTF-8
35
+ Transfer-Encoding:
36
+ - chunked
37
+ Vary:
38
+ - Accept-Encoding
39
+ Date:
40
+ - Mon, 07 Dec 2015 06:20:30 GMT
41
+ Server:
42
+ - AmazonEC2
43
+ body:
44
+ encoding: UTF-8
45
+ string: |-
46
+ <?xml version="1.0" encoding="UTF-8"?>
47
+ <TerminateInstancesResponse xmlns="http://ec2.amazonaws.com/doc/2015-10-01/">
48
+ <requestId>dd3e2501-6807-48ce-b8b7-e84a4a930cff</requestId>
49
+ <instancesSet>
50
+ <item>
51
+ <instanceId>i-e094e03f</instanceId>
52
+ <currentState>
53
+ <code>32</code>
54
+ <name>shutting-down</name>
55
+ </currentState>
56
+ <previousState>
57
+ <code>16</code>
58
+ <name>running</name>
59
+ </previousState>
60
+ </item>
61
+ </instancesSet>
62
+ </TerminateInstancesResponse>
63
+ http_version:
64
+ recorded_at: Mon, 07 Dec 2015 06:20:39 GMT
65
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,20 @@
1
+ require 'ec2_templater/version'
2
+ require 'ec2_templater/runner'
3
+ require 'ec2_templater/monitor'
4
+ require 'ec2_templater/command_line'
5
+
6
+ module Ec2Templater
7
+ def self.run(config)
8
+ monitor = Monitor.new(Runner.new(config), config['interval'])
9
+ monitor.run do
10
+ system(config['notify_cmd'])
11
+ end
12
+ monitor
13
+ end
14
+
15
+ def self.logger
16
+ @logger ||= Logger.new($stdout).tap do |log|
17
+ log.progname = 'ec2_templater'
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'clamp'
2
+ require 'yaml'
3
+
4
+ module Ec2Templater
5
+ class CommandLine < Clamp::Command
6
+ option ['-c', '--config'],
7
+ 'CONFIG',
8
+ 'Path to config file in YAML format',
9
+ required: true
10
+
11
+ def execute
12
+ validate_config_exists(config)
13
+ loaded_config = YAML.load_file(config)
14
+ Ec2Templater.run(loaded_config)
15
+ end
16
+
17
+ def validate_config_exists(config)
18
+ return if File.exist?(config)
19
+ signal_usage_error "Config at #{config} doesn't exist"
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require 'ec2_templater/filtered_ec2_list'
2
+
3
+ module Ec2Templater
4
+ class Ec2Grouper
5
+ # Provide a hash of groupnames to filters
6
+ def initialize(groups)
7
+ @filtered_ec2_list = FilteredEc2List.new
8
+ @groups = groups
9
+ end
10
+
11
+ # Returns a hash of groupnames to ec2 instance objects
12
+ def call
13
+ Hash[
14
+ @groups.map do |group_name, filters|
15
+ [group_name.to_sym, @filtered_ec2_list.call(filters)]
16
+ end
17
+ ]
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ require 'aws-sdk'
2
+
3
+ module Ec2Templater
4
+ class FilteredEc2List
5
+ # Provide an awssdk v2 client
6
+ def initialize(client = Aws::EC2::Client.new)
7
+ @client = client
8
+ end
9
+
10
+ # Return list of instances that match the provided filters.
11
+ #
12
+ # The filters match those defined for the describe-instances api call.
13
+ # http://docs.aws.amazon.com/cli/latest/reference/ec2/describe-instances.html
14
+ #
15
+ # The returned instances are <Types::Instance> from awssdk. See:
16
+ # http://docs.aws.amazon.com/sdkforruby/api/Aws/EC2/Types/Instance.html
17
+ #
18
+ # Filter Examples:
19
+ # { "tag:Name" => "", "vpc-id" => "vpc-9321c9a2" }
20
+ # { "tag:Environment" => "Production" }
21
+ def call(filters = {})
22
+ args = { filters: processed_filters(default_filters.merge(filters)) }
23
+ @client.describe_instances(args).reservations.flat_map(&:instances)
24
+ end
25
+
26
+ def [](filters = {})
27
+ call(filters)
28
+ end
29
+
30
+ def processed_filters(filters)
31
+ filters.map do |key, value|
32
+ { name: key, values: Array(value) }
33
+ end
34
+ end
35
+
36
+ def default_filters
37
+ { 'instance-state-name' => 'running' }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,30 @@
1
+ module Ec2Templater
2
+ class Monitor
3
+ def initialize(command, interval)
4
+ @command = command
5
+ @interval = interval
6
+ end
7
+
8
+ def run
9
+ Ec2Templater.logger.info 'Monitoring'
10
+ setup_signal_handlers
11
+ loop do
12
+ command_result = @command.call
13
+ if command_result.changed?
14
+ Ec2Templater.logger.info 'Notifying change'
15
+ yield(command_result)
16
+ end
17
+ sleep @interval
18
+ end
19
+ end
20
+
21
+ def setup_signal_handlers
22
+ %w(INT TERM).each do |signal|
23
+ trap signal do
24
+ Ec2Templater.logger.info "Recieved SIG#{signal}, shutting down."
25
+ exit 0
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ require 'erubis'
2
+
3
+ module Ec2Templater
4
+ class Renderer
5
+ Result = Struct.new(:changed?, :content)
6
+
7
+ def initialize(template, target)
8
+ @template_renderer = Erubis::Eruby.new(template)
9
+ @target = target
10
+ end
11
+
12
+ def call(vars)
13
+ new_content = render(vars)
14
+ changed = (target_content != new_content)
15
+ write_target(new_content) if changed
16
+ Result.new(changed, new_content)
17
+ end
18
+
19
+ private
20
+
21
+ def render(vars)
22
+ @template_renderer.evaluate(vars)
23
+ end
24
+
25
+ def target_content
26
+ File.read(@target) if File.exist?(@target)
27
+ end
28
+
29
+ def write_target(content)
30
+ File.write(@target, content)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,20 @@
1
+ require 'ec2_templater/renderer'
2
+ require 'ec2_templater/ec2_grouper'
3
+
4
+ module Ec2Templater
5
+ class Runner
6
+ def initialize(config)
7
+ @vars = config.fetch('vars', {})
8
+ @renderer = Renderer.new(File.read(config['template']), config['target'])
9
+ @ec2_grouper = Ec2Grouper.new(config['groups'])
10
+ end
11
+
12
+ def call
13
+ @renderer.call(vars)
14
+ end
15
+
16
+ def vars
17
+ @vars.merge(groups: @ec2_grouper.call)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module Ec2Templater
2
+ VERSION = '0.1.0'
3
+ end
metadata ADDED
@@ -0,0 +1,218 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ec2_templater
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Adam Davies
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aws-sdk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: erubis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: clamp
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.10'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.10'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.4'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.4'
97
+ - !ruby/object:Gem::Dependency
98
+ name: vcr
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '2.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '2.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.24'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.24'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.37'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.37'
139
+ - !ruby/object:Gem::Dependency
140
+ name: pry-byebug
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.3'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '3.3'
153
+ description: Ec2Templater provides a means of EC2 service discovery by templating
154
+ your server config. It works by periodically querying AWS for the list of running
155
+ EC2 instances, rendering a template, then running a notify command if it has changed.
156
+ Using this setup you can provide, for example, a haproxy config that updates based
157
+ on instances that are available filtered by tag
158
+ email:
159
+ - adzdavies@gmail.com
160
+ - rubygems@reinteractive.net
161
+ executables:
162
+ - ec2_templater
163
+ extensions: []
164
+ extra_rdoc_files: []
165
+ files:
166
+ - ".gitignore"
167
+ - ".rspec"
168
+ - ".rubocop.yml"
169
+ - ".travis.yml"
170
+ - CODE_OF_CONDUCT.md
171
+ - Gemfile
172
+ - LICENSE.txt
173
+ - README.md
174
+ - Rakefile
175
+ - bin/console
176
+ - bin/setup
177
+ - ec2_templater.gemspec
178
+ - example/config.yml
179
+ - example/haproxy.cfg.erb
180
+ - exe/ec2_templater
181
+ - fixtures/vcr_cassettes/Executing_ec2_templater_with_example_config/will_write_the_rendered_template.yml
182
+ - fixtures/vcr_cassettes/Filtered_Ec2_List_on_matching_tags/.yml
183
+ - fixtures/vcr_cassettes/Filtered_Ec2_List_on_matching_tags/when_filters_do_not_match/.yml
184
+ - fixtures/vcr_cassettes/ec2_run_instance.yml
185
+ - fixtures/vcr_cassettes/ec2_terminate_instance.yml
186
+ - lib/ec2_templater.rb
187
+ - lib/ec2_templater/command_line.rb
188
+ - lib/ec2_templater/ec2_grouper.rb
189
+ - lib/ec2_templater/filtered_ec2_list.rb
190
+ - lib/ec2_templater/monitor.rb
191
+ - lib/ec2_templater/renderer.rb
192
+ - lib/ec2_templater/runner.rb
193
+ - lib/ec2_templater/version.rb
194
+ homepage: https://github.com/reinteractive/ec2_templater
195
+ licenses:
196
+ - MIT
197
+ metadata: {}
198
+ post_install_message:
199
+ rdoc_options: []
200
+ require_paths:
201
+ - lib
202
+ required_ruby_version: !ruby/object:Gem::Requirement
203
+ requirements:
204
+ - - ">="
205
+ - !ruby/object:Gem::Version
206
+ version: '0'
207
+ required_rubygems_version: !ruby/object:Gem::Requirement
208
+ requirements:
209
+ - - ">="
210
+ - !ruby/object:Gem::Version
211
+ version: '0'
212
+ requirements: []
213
+ rubyforge_project:
214
+ rubygems_version: 2.4.8
215
+ signing_key:
216
+ specification_version: 4
217
+ summary: EC2 service discovery by templating your server config
218
+ test_files: []