jackal-copperegg 0.1.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.
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: