jackal-copperegg 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 96fb71981b676056dcff2b278f68e715d7f35872
4
+ data.tar.gz: dd6fcde30ddf58eeb08531cb0fbe136ba0607f38
5
+ SHA512:
6
+ metadata.gz: ece2dc63ee8c4aaf0c69fda8ed75a70f6dc38ea616e7c7c4f74c051b8ddc4de9cd4a0ae9a98e855c764554164cd974056e6daab4f3bd7677c603055410613a9c
7
+ data.tar.gz: efa91ceb49b057909b8ca66e9ef30bd29b214438debd51271685f6be9fd383ac775f09bb4f00636a05a5b31e716a84f9a620a4f15f98d046c7ec0518bac8f39a
data/CHANGELOG.md ADDED
@@ -0,0 +1,2 @@
1
+ # v0.1.0
2
+ * Initial release
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,25 @@
1
+ # Contributing
2
+
3
+ ## Branches
4
+
5
+ ### `master` branch
6
+
7
+ The master branch is the current stable released version.
8
+
9
+ ### `develop` branch
10
+
11
+ The develop branch is the current edge of development.
12
+
13
+ ## Pull requests
14
+
15
+ * https://github.com/carnivore-rb/jackal-copperegg/pulls
16
+
17
+ Please base all pull requests of the `develop` branch. Merges to
18
+ `master` only occur through the `develop` branch. Pull requests
19
+ based on `master` will likely be cherry picked.
20
+
21
+ ## Issues
22
+
23
+ Need to report an issue? Use the github issues:
24
+
25
+ * https://github.com/carnivore-rb/jackal-copperegg/issues
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 Chris Roberts
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ # CFN Copperegg helpers
@@ -0,0 +1,17 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__)) + '/lib/'
2
+ require 'jackal-copperegg/version'
3
+ Gem::Specification.new do |s|
4
+ s.name = 'jackal-copperegg'
5
+ s.version = Jackal::Copperegg::VERSION.version
6
+ s.summary = 'Message processing helper'
7
+ s.author = 'Chris Roberts'
8
+ s.email = 'code@chrisroberts.org'
9
+ s.homepage = 'https://github.com/carnivore-rb/jackal-copperegg'
10
+ s.description = 'Copperegg callback helpers'
11
+ s.require_path = 'lib'
12
+ s.license = 'Apache 2.0'
13
+ s.add_dependency 'jackal'
14
+ s.add_dependency 'jackal-cfn'
15
+ s.add_dependency 'patron'
16
+ s.files = Dir['lib/**/*'] + %w(jackal-copperegg.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
17
+ end
@@ -0,0 +1,9 @@
1
+ require 'jackal-cfn'
2
+ require 'jackal-copperegg/version'
3
+
4
+ module Jackal
5
+ module CfnTools
6
+ autoload :CoppereggProbe, 'jackal-copperegg/resources/probe'
7
+ autoload :CoppereggStackServerCleanup, 'jackal-copperegg/events/stack_server_cleanup'
8
+ end
9
+ end
@@ -0,0 +1,94 @@
1
+ require 'jackal-copperegg'
2
+
3
+ module Jackal
4
+ module CfnTools
5
+
6
+ # Removes servers from revealcloud on stack deletion. Depends
7
+ # on servers being registered with the CFN stack ID tagged on
8
+ # it.
9
+ class CoppereggStackServerCleanup < Jackal::Cfn::Event
10
+
11
+ # Validity of message
12
+ #
13
+ # @param message [Carnivore::Message]
14
+ # @return [TrueClass, FalseClass]
15
+ def valid?(message)
16
+ super do |payload|
17
+ payload.get(:data, :cfn_event, :resource_type) == 'AWS::CloudFormation::Stack' &&
18
+ payload.get(:data, :cfn_event, :resource_status) == 'DELETE_COMPLETE'
19
+ end
20
+ end
21
+
22
+ # Delete any revealcloud servers
23
+ #
24
+ # @param message [Carnivore::Message]
25
+ def execute(message)
26
+ failure_wrap(message) do |payload|
27
+ event = payload.get(:data, :cfn_event)
28
+ url = URI.parse(
29
+ config.fetch(
30
+ :copperegg,
31
+ :revealcloud_url,
32
+ 'https://api.copperegg.com'
33
+ )
34
+ )
35
+ endpoint = http_endpoint(url.host, url.scheme)
36
+ endpoint.username = config.get(:copperegg, :revealcloud_username)
37
+ endpoint.password = config.get(:copperegg, :revealcloud_password)
38
+ endpoint.connect_timeout = 30
39
+ endpoint.timeout = 30
40
+ destroy_revealcloud_servers(event[:stack_id], endpoint)
41
+ completed(payload, message)
42
+ end
43
+ end
44
+
45
+ # Search for all revealcloud servers registered
46
+ # with provided stack ID
47
+ #
48
+ # @param stack_id [String]
49
+ # @param endpoint [Patron::Session]
50
+ # @return [Array<Hash>] servers deleted
51
+ # @todo catch HTTP related errors, log and continue on
52
+ def destroy_revealcloud_servers(stack_id, endpoint)
53
+ servers = detect_revealcloud_servers(stack_id, endpoint)
54
+ debug "Number of servers detected to remove from revealcloud: #{servers.count}"
55
+ servers.each do |server|
56
+ info "Removing server instance from revealcloud: #{server[:uuid]}"
57
+ debug "Server instance information: #{server.inspect}"
58
+ endpoint.delete(
59
+ File.join(
60
+ config.fetch(
61
+ :copperegg,
62
+ :revealcloud_delete_path,
63
+ '/v2/revealcloud/uuids'
64
+ ),
65
+ "#{server[:uuid]}.json"
66
+ )
67
+ )
68
+ end
69
+ servers
70
+ end
71
+
72
+ # Find all revealcloud servers tagged with stack id
73
+ #
74
+ # @param stack_id [String]
75
+ # @param endpoint [Patron::Session]
76
+ # @return [Array<Hash>] matching servers
77
+ def detect_revealcloud_servers(stack_id, endpoint)
78
+ response = endpoint.get(
79
+ config.fetch(
80
+ :copperegg,
81
+ :revealcloud_index_path,
82
+ '/v2/revealcloud/systems.json?show_hidden=1'
83
+ )
84
+ )
85
+ servers = MultiJson.load(response.body)
86
+ servers.find_all do |server|
87
+ server.fetch('t', []).include?(stack_id)
88
+ end
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,190 @@
1
+ require 'base64'
2
+ require 'jackal-copperegg'
3
+
4
+ module Jackal
5
+ module CfnTools
6
+ # Add or remove probes from a stack
7
+ #
8
+ # Expected resource:
9
+ # {
10
+ # "Type": "Custom::CoppereggProbe",
11
+ # "Properties": {
12
+ # "Parameters": {
13
+ # "Probes": [
14
+ # {
15
+ # ## required
16
+ # "ProbeDesc": "A description",
17
+ # "Type": "GET", # or POST, TCP, IMCP
18
+ # "ProbeDest": "http://localhost",
19
+ # ## optional
20
+ # "CheckContents": "match", # or nomatch
21
+ # "ContentMatch": "StringForComparison",
22
+ # "Frequency": 30,
23
+ # "Timeout": 10000,
24
+ # "Retries": 3,
25
+ # "Tags": "comma,delimited,tags",
26
+ # "Stations": ["LON"]
27
+ # }
28
+ # ]
29
+ # }
30
+ # }
31
+ # }
32
+ # @see http://dev.copperegg.com/revealuptime/probes.html
33
+ class CoppereggProbe < Jackal::Cfn::Resource
34
+
35
+ # Process probe related action
36
+ #
37
+ # @param message [Carnivore::Message]
38
+ def execute(message)
39
+ failure_wrap(message) do |payload|
40
+ cfn_resource = rekey_hash(payload.get(:data, :cfn_resource))
41
+ properties = rekey_hash(cfn_resource[:resource_properties])
42
+ parameters = rekey_hash(properties[:parameters])
43
+ cfn_response = build_response(cfn_resource)
44
+ case cfn_resource[:request_type].to_sym
45
+ when :create
46
+ create_probes(
47
+ :probes => [parameters[:probes]].flatten,
48
+ :response => cfn_response
49
+ )
50
+ when :update
51
+ error 'Update requested but not supported!'
52
+ cfn_response['Status'] = 'FAILED'
53
+ cfn_response['Reason'] = 'Updates are not supported on this resource'
54
+ when :delete
55
+ delete_probes(
56
+ :physical_resource_id => cfn_resource[:physical_resource_id],
57
+ :response => cfn_response
58
+ )
59
+ else
60
+ error "Unknown request type received: #{payload[:request_type]}"
61
+ cfn_response['Status'] = 'FAILED'
62
+ cfn_response['Reason'] = 'Unknown request type received'
63
+ end
64
+ respond_to_stack(cfn_response, cfn_resource[:response_url])
65
+ completed(payload, message)
66
+ end
67
+ end
68
+
69
+ # Create new probes
70
+ #
71
+ # @param args [Hash]
72
+ # @option args [Array<Hash>] :probes probes to create
73
+ # @option args [Hash] :response cfn response message
74
+ # @return [TrueClass]
75
+ def create_probes(args={})
76
+ endpoint = generate_endpoint
77
+ results = []
78
+ args[:probes].each do |probe|
79
+ probe_data = MultiJson.dump(rekey_hash(probe))
80
+ debug "Attempting probe creation (#{probe_data.inspect})"
81
+ results.push(
82
+ endpoint.post(
83
+ config.fetch(
84
+ :copperegg,
85
+ :revealuptime_create_path,
86
+ '/v2/revealuptime/probes.json'
87
+ ),
88
+ probe_data,
89
+ 'Content-Type' => 'application/json'
90
+ )
91
+ )
92
+ end
93
+ errors = results.find_all do |result|
94
+ result.status != 200
95
+ end
96
+ results -= errors
97
+ results.map! do |result|
98
+ MultiJson.load(result.body)['id']
99
+ end
100
+ if(errors.empty?)
101
+ debug "Probes created: #{results.inspect}"
102
+ args[:response]['PhysicalResourceId'] = Base64.urlsafe_encode64(
103
+ MultiJson.dump(results)
104
+ )
105
+ args[:response]['Status'] = 'SUCCESS'
106
+ args[:response]['Data']['Reason'] = "New copperegg probes added: #{results.size}"
107
+ else
108
+ probe_deletion(*results)
109
+ args[:response]['Status'] = 'FAILED'
110
+ args[:response]['Reason'] = "Probe creation failed: #{errors.map(&:body).map(&:strip).join(', ')}"
111
+ end
112
+ true
113
+ end
114
+
115
+ # Call API to delete probes
116
+ #
117
+ # @param ids [String] probe id list
118
+ # @return [Array<Patron::Response>]
119
+ def probe_deletion(*ids)
120
+ endpoint = generate_endpoint
121
+ ids.map do |probe_id|
122
+ debug "Copperegg probe deletion ID: #{probe_id}"
123
+ if(probe_id.to_s.strip.empty?)
124
+ error 'Encountered an empty probe ID. Skipping.'
125
+ nil
126
+ else
127
+ delete_path = File.join(
128
+ config.fetch(
129
+ :copperegg,
130
+ :revealuptime_delete_path,
131
+ '/v2/revealuptime/probes'
132
+ ),
133
+ "#{probe_id}.json"
134
+ )
135
+ endpoint.delete(delete_path)
136
+ end
137
+ end.compact
138
+ end
139
+
140
+ # Delete probes
141
+ #
142
+ # @param args [Hash]
143
+ # @option args [String] :physical_resource_id probes to delete
144
+ # @option args [Hash] :response cfn response message
145
+ # @return [TrueClass]
146
+ def delete_probes(args={})
147
+ begin
148
+ ids = MultiJson.load(
149
+ Base64.urlsafe_decode64(
150
+ args[:physical_resource_id]
151
+ )
152
+ )
153
+ results = probe_deletion(*ids)
154
+ errors = results.find_all do |r|
155
+ r.status != 200 && r.status != 404
156
+ end
157
+ results -= errors
158
+ if(errors.empty?)
159
+ args[:response]['Status'] = 'SUCCESS'
160
+ args[:response]['Data']['Reason'] = "Probes removed (#{ids.count})"
161
+ else
162
+ args[:response]['Status'] = 'FAILED'
163
+ args[:response]['Reason'] = "Probe deletion failed: #{errors.map(&:body).map(&:strip).join(', ')}"
164
+ end
165
+ rescue ArgumentError, MultiJson::ParseError
166
+ args[:response]['Status'] = 'SUCCESS'
167
+ args[:response]['Data']['Reason'] = 'No probes to delete!'
168
+ end
169
+ true
170
+ end
171
+
172
+ # @return [Patron::Session]
173
+ def generate_endpoint
174
+ url = URI.parse(
175
+ config.fetch(
176
+ :copperegg,
177
+ :revealuptime_url,
178
+ 'https://api.copperegg.com'
179
+ )
180
+ )
181
+ endpoint = http_endpoint(url.host, url.scheme)
182
+ endpoint.username = config.get(:copperegg, :revealuptime_username)
183
+ endpoint.password = config.get(:copperegg, :revealuptime_password)
184
+ endpoint
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+ end
@@ -0,0 +1,6 @@
1
+ module Jackal
2
+ module Copperegg
3
+ # Current library version
4
+ VERSION = Gem::Version.new('0.1.0')
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jackal-copperegg
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Roberts
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: jackal
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: jackal-cfn
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: patron
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Copperegg callback helpers
56
+ email: code@chrisroberts.org
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - CHANGELOG.md
62
+ - CONTRIBUTING.md
63
+ - LICENSE
64
+ - README.md
65
+ - jackal-copperegg.gemspec
66
+ - lib/jackal-copperegg.rb
67
+ - lib/jackal-copperegg/events/stack_server_cleanup.rb
68
+ - lib/jackal-copperegg/resources/probe.rb
69
+ - lib/jackal-copperegg/version.rb
70
+ homepage: https://github.com/carnivore-rb/jackal-copperegg
71
+ licenses:
72
+ - Apache 2.0
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.2.2
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: Message processing helper
94
+ test_files: []
95
+ has_rdoc: