bigpanda 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/bigpanda.rb +1 -0
- data/lib/bigpanda/bp-api.rb +134 -0
- data/lib/bigpanda/capistrano.rb +81 -0
- metadata +63 -0
data/lib/bigpanda.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bigpanda/bp-api'
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'net/https'
|
4
|
+
|
5
|
+
module BigPanda
|
6
|
+
|
7
|
+
VERSION = '0.1.0'
|
8
|
+
|
9
|
+
DEFAULT_CONFIG_FILES = [ "/etc/bigpanda.yaml", "/etc/bigpanda.yml" ]
|
10
|
+
DEFAULT_CONFIGURATION = { # overridable via the configuration file
|
11
|
+
:access_token => nil,
|
12
|
+
:target_url => 'https://api.bigpanda.io',
|
13
|
+
:deployment_start_path => '/data/events/deployments/start',
|
14
|
+
:deployment_end_path => '/data/events/deployments/end'
|
15
|
+
}
|
16
|
+
|
17
|
+
#
|
18
|
+
# Main BigPanda API Class
|
19
|
+
#
|
20
|
+
# The client needs to be initialized with the access_token of the organization.
|
21
|
+
# Possible options:
|
22
|
+
# 1. Pass the access_token as a :access_token parameter
|
23
|
+
# 2. Place a bigpanda.yaml file in /etc/ and add to the file - 'access_token': YOUR_TOKEN
|
24
|
+
# 3. Pass a location of a yaml file using the :file parameter
|
25
|
+
#
|
26
|
+
#
|
27
|
+
class Client
|
28
|
+
attr_reader :config, :ssl
|
29
|
+
|
30
|
+
def initialize(options = {})
|
31
|
+
|
32
|
+
unless options.fetch(:access_token, nil)
|
33
|
+
file = options.delete(:file)
|
34
|
+
file_config, config_files = read_config_from_file(file)
|
35
|
+
options.merge!(file_config) if file_config
|
36
|
+
end
|
37
|
+
|
38
|
+
@config = DEFAULT_CONFIGURATION.merge(options)
|
39
|
+
@ssl = options.delete(:ssl) || {}
|
40
|
+
|
41
|
+
unless @config.fetch(:access_token, nil)
|
42
|
+
raise "No BigPanda config token received, and no configuration file found. Searched: #{config_files.join ','}."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def deployment_start(options = {})
|
47
|
+
return deployment_notification config[:deployment_start_path], options
|
48
|
+
end
|
49
|
+
|
50
|
+
def deployment_end(options = {})
|
51
|
+
return deployment_notification config[:deployment_end_path], options
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
class Error < RuntimeError; end
|
57
|
+
class Unauthorized < RuntimeError; end
|
58
|
+
|
59
|
+
# Prepare the call to BigPanda API and send a HTTP Request
|
60
|
+
#
|
61
|
+
def deployment_notification(path, options)
|
62
|
+
place_default_values(options)
|
63
|
+
|
64
|
+
return request path, JSON.generate(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Perform the actual request to BigPanda API
|
68
|
+
#
|
69
|
+
def request(path, body, target_url = config[:target_url])
|
70
|
+
headers = { 'Accept' => 'application/json',
|
71
|
+
'Content-Type' => 'application/json',
|
72
|
+
'Authorization' => "Bearer #{config[:access_token]}" }
|
73
|
+
|
74
|
+
uri = URI.parse(target_url)
|
75
|
+
h = Net::HTTP.new uri.host, uri.port
|
76
|
+
h.set_debug_output $stderr if $DEBUG
|
77
|
+
h.use_ssl = (uri.scheme.downcase == 'https')
|
78
|
+
|
79
|
+
set_ssl_overrides(h)
|
80
|
+
|
81
|
+
h.start do |http|
|
82
|
+
response = http.request_post path, body, headers
|
83
|
+
raise Unauthorized if response.code.to_i == 401
|
84
|
+
raise Error, "server error: #{response.body}" if response.code.to_i >= 500
|
85
|
+
|
86
|
+
answer = JSON.load(response.body)['response']
|
87
|
+
raise Error, answer['errors'].join(", ") if response.code.to_i >= 400
|
88
|
+
|
89
|
+
return answer
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def place_default_values(options)
|
94
|
+
options[:timestamp] = options.fetch(:timestamp, Time.now.utc.to_i) # unix time
|
95
|
+
|
96
|
+
if options.has_key?(:hosts)
|
97
|
+
options[:hosts] = options[:hosts].kind_of?(Array) ? options[:hosts] : [options[:hosts]]
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Read configuration from the default list of configuration files and the received file.
|
102
|
+
#
|
103
|
+
# Returns the the found configuration and the list of files which were used.
|
104
|
+
#
|
105
|
+
def read_config_from_file(file)
|
106
|
+
|
107
|
+
config_files = ([file] + DEFAULT_CONFIG_FILES).compact
|
108
|
+
config = nil
|
109
|
+
|
110
|
+
config_files.each do |config_file|
|
111
|
+
if File.exists? config_file
|
112
|
+
config = YAML.load_file(config_file)
|
113
|
+
|
114
|
+
# Convert string keys to symbols
|
115
|
+
config.keys.each do |key|
|
116
|
+
config[(key.to_sym rescue key) || key] = config.delete(key)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
return config, config_files
|
122
|
+
end
|
123
|
+
|
124
|
+
def set_ssl_overrides(http)
|
125
|
+
http.certificate = ssl[:client_cert] if ssl[:client_cert]
|
126
|
+
http.private_key = ssl[:client_key] if ssl[:client_key]
|
127
|
+
http.ca_file = ssl[:ca_file] if ssl[:ca_file]
|
128
|
+
http.ssl_version = ssl[:version] if ssl[:version]
|
129
|
+
http.verify_mode = ssl[:verify_mode] if ssl[:verify_mode]
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'bigpanda'
|
2
|
+
|
3
|
+
Capistrano::Configuration.instance(:must_exist).load do |config|
|
4
|
+
|
5
|
+
namespace :bigpanda do
|
6
|
+
|
7
|
+
desc '[Internal] Notifies BigPanda that a deployment has started'
|
8
|
+
task :notify_deploy_started do
|
9
|
+
transaction do
|
10
|
+
on_rollback do
|
11
|
+
notify_deploy_failed
|
12
|
+
end
|
13
|
+
|
14
|
+
send_deployment_start
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc '[Internal] Notifies BigPanda that a deployment has finished successfuly'
|
19
|
+
task :notify_deploy_finished do
|
20
|
+
send_deployment_end('success')
|
21
|
+
end
|
22
|
+
|
23
|
+
desc '[Internal] Notifies BigPanda that a deployment has finished with an error'
|
24
|
+
task :notify_deploy_failed do
|
25
|
+
send_deployment_end('failure', errorMessage: 'failed to deploy')
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
# Map capistrano execution variables to BigPanda Fields for deployment start
|
31
|
+
#
|
32
|
+
# == Parameters:
|
33
|
+
# optional Hash which can contain all propriatery fields
|
34
|
+
#
|
35
|
+
def send_deployment_start(properties = {})
|
36
|
+
panda = create_bigpanda_client
|
37
|
+
|
38
|
+
panda.deployment_start({:component => application,
|
39
|
+
:version => "#{fetch(:branch, '')} #{release_name}",
|
40
|
+
:hosts => find_servers_for_task(current_task),
|
41
|
+
:env => rails_env,
|
42
|
+
:owner => fetch(:bp_owner, nil),
|
43
|
+
:properties => properties
|
44
|
+
})
|
45
|
+
rescue Exception => e
|
46
|
+
logger.important "err :: while sending BigPanda start, Skipping to next command. #{e.message}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# Map capistrano execution variables to BigPanda Fields for deployment end
|
50
|
+
#
|
51
|
+
# == Parameters:
|
52
|
+
# optional Hash which can contain all propriatery fields
|
53
|
+
#
|
54
|
+
def send_deployment_end(status, properties = {})
|
55
|
+
panda = create_bigpanda_client
|
56
|
+
|
57
|
+
errorMessage = properties.delete(:errorMessage)
|
58
|
+
|
59
|
+
panda.deployment_end({ :component => application,
|
60
|
+
:version => "#{fetch(:branch, '')} #{release_name}",
|
61
|
+
:hosts => find_servers_for_task(current_task),
|
62
|
+
:status => status,
|
63
|
+
:env => rails_env,
|
64
|
+
:errorMessage => errorMessage,
|
65
|
+
:properties => properties})
|
66
|
+
rescue Exception => e
|
67
|
+
logger.important "err :: while sending BigPanda start, Skipping to next command. #{e.message}"
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
def create_bigpanda_client
|
72
|
+
BigPanda::Client.new(:access_token => fetch(:bigpanda_access_token, nil))
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Hooks:
|
77
|
+
# Wrap deploy execution
|
78
|
+
|
79
|
+
before "deploy:update_code", "bigpanda:notify_deploy_started"
|
80
|
+
after "deploy", "bigpanda:notify_deploy_finished"
|
81
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bigpanda
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- BigPanda
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-07-17 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: json
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
description: A Ruby client (and additional integrations) for BigPanda's API
|
31
|
+
email: support@bigpanda.io
|
32
|
+
executables: []
|
33
|
+
extensions: []
|
34
|
+
extra_rdoc_files: []
|
35
|
+
files:
|
36
|
+
- lib/bigpanda.rb
|
37
|
+
- lib/bigpanda/capistrano.rb
|
38
|
+
- lib/bigpanda/bp-api.rb
|
39
|
+
homepage: https://github.com/bigpandaio/bigpanda-rb
|
40
|
+
licenses: []
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.8.24
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: A Ruby client (and additional integrations) for BigPanda's API
|
63
|
+
test_files: []
|