acquia-cloud 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +98 -0
- data/LICENSE +19 -0
- data/README.md +32 -0
- data/Rakefile +66 -0
- data/acquia-cloud.gemspec +31 -0
- data/lib/acquia/cloud.rb +41 -0
- data/lib/acquia/cloud/api.rb +56 -0
- data/lib/acquia/cloud/database.rb +22 -0
- data/lib/acquia/cloud/entity.rb +19 -0
- data/lib/acquia/cloud/environment.rb +80 -0
- data/lib/acquia/cloud/errors.rb +8 -0
- data/lib/acquia/cloud/logs/source.rb +17 -0
- data/lib/acquia/cloud/logs/streamer.rb +147 -0
- data/lib/acquia/cloud/server.rb +62 -0
- data/lib/acquia/cloud/site.rb +73 -0
- data/lib/acquia/cloud/task.rb +62 -0
- data/lib/acquia/cloud/version.rb +5 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 76d413a96d61a55b3bdd919b882bb1c2a51344e7
|
4
|
+
data.tar.gz: a4d6543e4d1c4036c00b5fb9cbde78e2af438929
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 70a6e0594f9248b8ba6348bce0c041a0c2ad0e4df6a1bd60493c4ed844d8a9564f06aee35e741194405d52ad665b68eba826f34cf75f3752755c18ea7a247e37
|
7
|
+
data.tar.gz: 43c45cbc34be2a07f6745b9e2f40628b313645b2a8c8f66e1d9f12aa4da05ce98c2b188fdef947b34c9b8d11a1bd462944fc1c0a87aa392fd02de775bfc77dd5
|
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
/pkg
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.2
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
acquia-cloud (0.1.0.pre)
|
5
|
+
faraday (~> 0.9)
|
6
|
+
websocket (~> 1.2)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
abstract_type (0.0.7)
|
12
|
+
adamantium (0.2.0)
|
13
|
+
ice_nine (~> 0.11.0)
|
14
|
+
memoizable (~> 0.4.0)
|
15
|
+
anima (0.3.0)
|
16
|
+
abstract_type (~> 0.0.7)
|
17
|
+
adamantium (~> 0.2)
|
18
|
+
equalizer (~> 0.0.11)
|
19
|
+
ast (2.1.0)
|
20
|
+
concord (0.1.5)
|
21
|
+
adamantium (~> 0.2.0)
|
22
|
+
equalizer (~> 0.0.9)
|
23
|
+
diff-lcs (1.2.5)
|
24
|
+
equalizer (0.0.11)
|
25
|
+
faraday (0.9.2)
|
26
|
+
multipart-post (>= 1.2, < 3)
|
27
|
+
ice_nine (0.11.1)
|
28
|
+
memoizable (0.4.2)
|
29
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
30
|
+
morpher (0.2.5)
|
31
|
+
abstract_type (~> 0.0.7)
|
32
|
+
adamantium (~> 0.2.0)
|
33
|
+
anima (~> 0.3.0)
|
34
|
+
ast (~> 2.1.0)
|
35
|
+
concord (~> 0.1.5)
|
36
|
+
equalizer (~> 0.0.9)
|
37
|
+
ice_nine (~> 0.11.0)
|
38
|
+
procto (~> 0.0.2)
|
39
|
+
multipart-post (2.0.0)
|
40
|
+
mutant (0.8.8)
|
41
|
+
abstract_type (~> 0.0.7)
|
42
|
+
adamantium (~> 0.2.0)
|
43
|
+
anima (~> 0.3.0)
|
44
|
+
ast (~> 2.1)
|
45
|
+
concord (~> 0.1.5)
|
46
|
+
diff-lcs (~> 1.2)
|
47
|
+
equalizer (~> 0.0.9)
|
48
|
+
ice_nine (~> 0.11.1)
|
49
|
+
memoizable (~> 0.4.2)
|
50
|
+
morpher (~> 0.2.5)
|
51
|
+
parallel (~> 1.3)
|
52
|
+
parser (~> 2.2.2)
|
53
|
+
procto (~> 0.0.2)
|
54
|
+
unparser (~> 0.2.4)
|
55
|
+
mutant-rspec (0.8.8)
|
56
|
+
mutant (~> 0.8.8)
|
57
|
+
rspec-core (>= 3.2.0, < 3.5.0)
|
58
|
+
parallel (1.6.1)
|
59
|
+
parser (2.2.3.0)
|
60
|
+
ast (>= 1.1, < 3.0)
|
61
|
+
procto (0.0.2)
|
62
|
+
rake (10.4.2)
|
63
|
+
rspec (3.4.0)
|
64
|
+
rspec-core (~> 3.4.0)
|
65
|
+
rspec-expectations (~> 3.4.0)
|
66
|
+
rspec-mocks (~> 3.4.0)
|
67
|
+
rspec-core (3.4.0)
|
68
|
+
rspec-support (~> 3.4.0)
|
69
|
+
rspec-expectations (3.4.0)
|
70
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
71
|
+
rspec-support (~> 3.4.0)
|
72
|
+
rspec-mocks (3.4.0)
|
73
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
74
|
+
rspec-support (~> 3.4.0)
|
75
|
+
rspec-support (3.4.0)
|
76
|
+
thread_safe (0.3.5)
|
77
|
+
unparser (0.2.4)
|
78
|
+
abstract_type (~> 0.0.7)
|
79
|
+
adamantium (~> 0.2.0)
|
80
|
+
concord (~> 0.1.5)
|
81
|
+
diff-lcs (~> 1.2.5)
|
82
|
+
equalizer (~> 0.0.9)
|
83
|
+
parser (~> 2.2.2)
|
84
|
+
procto (~> 0.0.2)
|
85
|
+
websocket (1.2.2)
|
86
|
+
|
87
|
+
PLATFORMS
|
88
|
+
ruby
|
89
|
+
|
90
|
+
DEPENDENCIES
|
91
|
+
acquia-cloud!
|
92
|
+
mutant (~> 0.8)
|
93
|
+
mutant-rspec (~> 0.8)
|
94
|
+
rake (~> 10.4)
|
95
|
+
rspec (~> 3.3)
|
96
|
+
|
97
|
+
BUNDLED WITH
|
98
|
+
1.10.6
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2015 Matthew Scharley
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
4
|
+
copy of this software and associated documentation files (the "Software"),
|
5
|
+
to deal in the Software without restriction, including without limitation
|
6
|
+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
7
|
+
and/or sell copies of the Software, and to permit persons to whom the
|
8
|
+
Software is furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included
|
11
|
+
in all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
14
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
18
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
19
|
+
DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# acquia-cloud
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/Equiem/acquia-cloud.svg)](https://travis-ci.org/Equiem/acquia-cloud)
|
4
|
+
[![Dependency Status](https://gemnasium.com/Equiem/acquia-cloud.svg)](https://gemnasium.com/Equiem/acquia-cloud)
|
5
|
+
|
6
|
+
**Source:** [https://github.com/Equiem/acquia-cloud](https://github.com/Equiem/acquia-cloud)
|
7
|
+
**Author:** [Equiem](http://equiem.com.au/)
|
8
|
+
**Contributors:** [See contributors on GitHub][gh-contrib]
|
9
|
+
**Bugs/Support:** [Github Issues][gh-issues]
|
10
|
+
**Copyright:** 2015
|
11
|
+
**License:** [MIT license][license]
|
12
|
+
**Status:** Active
|
13
|
+
|
14
|
+
## Synopsis
|
15
|
+
|
16
|
+
`acquia-cloud` is a Ruby binding for the Acquia Cloud API.
|
17
|
+
|
18
|
+
## Installation
|
19
|
+
|
20
|
+
$ gem install acquia-cloud
|
21
|
+
|
22
|
+
Or add `gem 'acquia-cloud'` to your Gemfile and update your bundle.
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
> TODO: Write this.
|
27
|
+
|
28
|
+
See also [the Cloud API documentation](https://cloudapi.acquia.com/).
|
29
|
+
|
30
|
+
[gh-contrib]: https://github.com/Equiem/acquia-cloud/graphs/contributors
|
31
|
+
[gh-issues]: https://github.com/Equiem/acquia-cloud/issues
|
32
|
+
[license]: https://github.com/Equiem/acquia-cloud/blob/master/LICENSE
|
data/Rakefile
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
$:.push File.join(__FILE__, '../lib')
|
2
|
+
require 'acquia/cloud/version'
|
3
|
+
|
4
|
+
def current_gems
|
5
|
+
Dir["pkg/acquia-cloud-#{Acquia::Cloud::VERSION}.gem"]
|
6
|
+
end
|
7
|
+
|
8
|
+
namespace :gem do
|
9
|
+
desc 'Build gem'
|
10
|
+
task :build => [:clean] do
|
11
|
+
mkdir 'pkg' unless File.exist? 'pkg'
|
12
|
+
sh *%w{gem build acquia-cloud.gemspec}
|
13
|
+
Dir['*.gem'].each do |gem|
|
14
|
+
mv gem, "pkg/#{gem}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Install gem'
|
19
|
+
task :install => ['gem:build'] do
|
20
|
+
sh *%W{gem install pkg/usmu-#{Acquia::Cloud::VERSION}.gem}
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Deploy gems to rubygems'
|
24
|
+
task :deploy => ['gem:build'] do
|
25
|
+
current_gems.each do |gem|
|
26
|
+
sh *%W{gem push #{gem}}
|
27
|
+
end
|
28
|
+
if File.exist? '.git'
|
29
|
+
sh *%W{git tag #{Acquia::Cloud::VERSION}}
|
30
|
+
sh *%W{git push origin tag #{Acquia::Cloud::VERSION}}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
task :clean
|
36
|
+
|
37
|
+
desc 'Run CI tasks.'
|
38
|
+
task :ci => [:rspec]
|
39
|
+
|
40
|
+
desc 'Run all RSpec tests.'
|
41
|
+
task :rspec do
|
42
|
+
sh 'bundle', 'exec', 'rspec'
|
43
|
+
end
|
44
|
+
|
45
|
+
desc 'Run mutation tests'
|
46
|
+
task :mutant, [:target] do |t,args|
|
47
|
+
sh 'bundle', 'exec', 'mutant',
|
48
|
+
'--include', 'lib',
|
49
|
+
'--require', 'acquia/cloud',
|
50
|
+
'--use', 'rspec',
|
51
|
+
# Interfaces and documentation classes
|
52
|
+
# '--ignore-subject', 'Usmu::Deployment::RemoteFileInterface*',
|
53
|
+
args[:target] || 'Acquia::Cloud*'
|
54
|
+
end
|
55
|
+
|
56
|
+
desc 'Run a console with our gems available'
|
57
|
+
task :console do
|
58
|
+
sh 'bundle', 'exec', 'irb', '-r', 'acquia/cloud'
|
59
|
+
end
|
60
|
+
|
61
|
+
namespace :git do
|
62
|
+
desc 'Cleanup (delete) all local branches that have already been merged into master (locally)'
|
63
|
+
task :cleanup do
|
64
|
+
`git branch --merged master`.lines.map(&:chomp).select {|i| i != ' master' && i[0,2] != '* ' }.each {|b| sh "git branch -d #{b.strip}" }
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
$:.push File.join(__FILE__, '../lib')
|
4
|
+
require 'acquia/cloud/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'acquia-cloud'
|
8
|
+
spec.version = Acquia::Cloud::VERSION
|
9
|
+
spec.authors = ['Equiem']
|
10
|
+
spec.email = ['sysadmin@equiem.com.au']
|
11
|
+
spec.summary = %q{Bindings to the Acquia Cloud API.}
|
12
|
+
spec.description = ''
|
13
|
+
spec.homepage = 'https://github.com/Equiem/BOTS'
|
14
|
+
spec.licenses = ['MIT']
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^spec/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.required_ruby_version = Gem::Requirement.new('~> 2.1')
|
22
|
+
|
23
|
+
spec.add_dependency 'faraday', '~> 0.9'
|
24
|
+
spec.add_dependency 'websocket', '~> 1.2'
|
25
|
+
|
26
|
+
# Dev stuff.
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.4'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.3'
|
29
|
+
spec.add_development_dependency 'mutant', '~> 0.8'
|
30
|
+
spec.add_development_dependency 'mutant-rspec', '~> 0.8'
|
31
|
+
end
|
data/lib/acquia/cloud.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Acquia
|
5
|
+
# @see https://cloudapi.acquia.com/
|
6
|
+
class Cloud
|
7
|
+
attr_reader :api
|
8
|
+
|
9
|
+
def initialize(args = {})
|
10
|
+
@api = Api.new args
|
11
|
+
end
|
12
|
+
|
13
|
+
def sites
|
14
|
+
@sites ||= @api.get('/sites').map do |site|
|
15
|
+
Site.new(self, site)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Acquia::Cloud::Site]
|
20
|
+
def site(name)
|
21
|
+
Site.new(self, name).refresh
|
22
|
+
end
|
23
|
+
|
24
|
+
def drushrc
|
25
|
+
@api.get '/me/drushrc'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
%w{
|
31
|
+
version
|
32
|
+
errors
|
33
|
+
api
|
34
|
+
database
|
35
|
+
environment
|
36
|
+
server
|
37
|
+
site
|
38
|
+
task
|
39
|
+
logs/source
|
40
|
+
logs/streamer
|
41
|
+
}.each {|p| require "acquia/cloud/#{p}" }
|
@@ -0,0 +1,56 @@
|
|
1
|
+
|
2
|
+
module Acquia
|
3
|
+
class Cloud
|
4
|
+
class Api
|
5
|
+
API_VERSION = 'v1'
|
6
|
+
BASE_URL = 'https://cloudapi.acquia.com'
|
7
|
+
|
8
|
+
def initialize(args = {})
|
9
|
+
@acquia = Faraday.new(:url => 'https://cloudapi.acquia.com') do |client|
|
10
|
+
client.request :url_encoded
|
11
|
+
client.adapter Faraday.default_adapter
|
12
|
+
|
13
|
+
# Log request and response to STDOUT.
|
14
|
+
# client.use Faraday::Response::Logger
|
15
|
+
|
16
|
+
# Raise errors on HTTP error codes.
|
17
|
+
client.use Faraday::Response::RaiseError
|
18
|
+
end
|
19
|
+
|
20
|
+
if args[:credentials].nil?
|
21
|
+
if ENV['ACQUIA_CLOUD_CREDENTIALS'].nil?
|
22
|
+
raise 'Unable to detect your credentials. Please populate ACQUIA_CLOUD_CREDENTIALS with your username and API key.'
|
23
|
+
end
|
24
|
+
|
25
|
+
args[:credentials] = ENV['ACQUIA_CLOUD_CREDENTIALS']
|
26
|
+
end
|
27
|
+
|
28
|
+
credentials = args[:credentials].split(':', 2)
|
29
|
+
if credentials.length < 2
|
30
|
+
raise 'You must specify both username and API key in credentials.'
|
31
|
+
end
|
32
|
+
@acquia.basic_auth *credentials
|
33
|
+
end
|
34
|
+
|
35
|
+
def get(url = nil, params = nil, headers = nil, &block)
|
36
|
+
request :get, url, params, headers, &block
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def post(url = nil, body = nil, headers = nil, &block)
|
41
|
+
request :post, url, body.nil? ? body : body.to_json, headers, &block
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete(url = nil, params = nil, headers = nil, &block)
|
45
|
+
request :delete, url, params, headers, &block
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
|
50
|
+
def request(method, url = nil, arg2 = nil, headers = nil, &block)
|
51
|
+
response = @acquia.method(method).call "/#{API_VERSION}#{url}.json", arg2, headers, &block
|
52
|
+
JSON.load(response.body)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'acquia/cloud/entity'
|
2
|
+
|
3
|
+
module Acquia
|
4
|
+
class Cloud
|
5
|
+
class Database < Entity
|
6
|
+
def initialize(cloud, site, database)
|
7
|
+
@cloud = cloud
|
8
|
+
@site = site
|
9
|
+
@data = database
|
10
|
+
@url = "/sites/#{@site}/dbs/#{@data['name']}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
data['name']
|
15
|
+
end
|
16
|
+
|
17
|
+
def backup_on(environment)
|
18
|
+
@cloud.site(@site).environment(environment).backup_database(name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'acquia/cloud/entity'
|
2
|
+
|
3
|
+
module Acquia
|
4
|
+
class Cloud
|
5
|
+
class Environment < Entity
|
6
|
+
def initialize(cloud, site, environment)
|
7
|
+
@cloud = cloud
|
8
|
+
@site = site
|
9
|
+
@data = environment
|
10
|
+
@url = "/sites/#{@site}/envs/#{@data['name']}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def name
|
14
|
+
data['name']
|
15
|
+
end
|
16
|
+
|
17
|
+
def vcs_path
|
18
|
+
data['vcs_path']
|
19
|
+
end
|
20
|
+
|
21
|
+
def ssh_host
|
22
|
+
data['ssh_host']
|
23
|
+
end
|
24
|
+
|
25
|
+
def db_clusters
|
26
|
+
data['db_clusters']
|
27
|
+
end
|
28
|
+
|
29
|
+
def default_domain
|
30
|
+
data['default_domain']
|
31
|
+
end
|
32
|
+
|
33
|
+
def unix_username
|
34
|
+
data['unix_username']
|
35
|
+
end
|
36
|
+
|
37
|
+
# new_vcs_path should start with 'tags/' or 'heads/' to distinguish
|
38
|
+
# between tags or branches.
|
39
|
+
def deploy_path(new_vcs_path)
|
40
|
+
task = @cloud.api.post("#{@url}/code-deploy") do |response|
|
41
|
+
response.params['path'] = new_vcs_path
|
42
|
+
end
|
43
|
+
|
44
|
+
Task.new(@cloud, @site, task)
|
45
|
+
end
|
46
|
+
|
47
|
+
def backup_database(database_name)
|
48
|
+
task = @cloud.api.post("#{@url}/dbs/#{database_name}/backups")
|
49
|
+
Task.new @cloud, @site, task
|
50
|
+
end
|
51
|
+
|
52
|
+
def logstream
|
53
|
+
response = @cloud.api.get("#{@url}/logstream")
|
54
|
+
Logs::Streamer.new(response['url'], JSON.parse(response['msg']))
|
55
|
+
end
|
56
|
+
|
57
|
+
def servers
|
58
|
+
@cloud.api.get("#{url}/servers").map do |server|
|
59
|
+
Server.new(@cloud, @site, name, server)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def server(server)
|
64
|
+
Server.new(@cloud, @site, name, {'name' => server}).refresh
|
65
|
+
end
|
66
|
+
|
67
|
+
def database_servers
|
68
|
+
servers.select &:database?
|
69
|
+
end
|
70
|
+
|
71
|
+
def varnish_servers
|
72
|
+
servers.select &:varnish?
|
73
|
+
end
|
74
|
+
|
75
|
+
def web_servers
|
76
|
+
servers.select &:web?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Acquia
|
2
|
+
class Cloud
|
3
|
+
module Logs
|
4
|
+
class Source
|
5
|
+
attr_reader :server
|
6
|
+
attr_reader :type
|
7
|
+
attr_reader :display_type
|
8
|
+
|
9
|
+
def initialize(server, type, display_type)
|
10
|
+
@server = server
|
11
|
+
@type = type
|
12
|
+
@display_type = display_type
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'websocket'
|
3
|
+
require 'json'
|
4
|
+
require 'uri'
|
5
|
+
|
6
|
+
module Acquia
|
7
|
+
class Cloud
|
8
|
+
module Logs
|
9
|
+
# https://github.com/acquia/logstream/blob/master/README.md
|
10
|
+
class Streamer
|
11
|
+
# Try to retrieve 16KB blocks from the socket.
|
12
|
+
RECV_LENGTH = 16 * 1024
|
13
|
+
|
14
|
+
attr_reader :remote_server
|
15
|
+
|
16
|
+
def initialize(url, message)
|
17
|
+
@url = url
|
18
|
+
@message = message
|
19
|
+
@logs = []
|
20
|
+
@available = []
|
21
|
+
@enabled_types = []
|
22
|
+
end
|
23
|
+
|
24
|
+
def debug
|
25
|
+
@debug = true
|
26
|
+
end
|
27
|
+
|
28
|
+
def connect
|
29
|
+
raise StreamerConnectionError, 'Already connected.' unless @socket.nil?
|
30
|
+
|
31
|
+
@handshake = ::WebSocket::Handshake::Client.new url: @url
|
32
|
+
uri = ::URI.parse @url
|
33
|
+
@socket = TCPSocket.new uri.host, uri.port || 80
|
34
|
+
@socket.print @handshake.to_s
|
35
|
+
|
36
|
+
until @handshake.finished?
|
37
|
+
@handshake << @socket.gets
|
38
|
+
end
|
39
|
+
raise StreamerConnectionError, "Couldn't get a valid response while connecting to the remote server: #{@url}" unless @handshake.valid?
|
40
|
+
@incoming = WebSocket::Frame::Incoming::Client.new(version: @handshake.version)
|
41
|
+
|
42
|
+
# Initial authentication
|
43
|
+
send @message
|
44
|
+
end
|
45
|
+
|
46
|
+
def close
|
47
|
+
@socket.close
|
48
|
+
end
|
49
|
+
|
50
|
+
def sources
|
51
|
+
update
|
52
|
+
@available.clone
|
53
|
+
end
|
54
|
+
|
55
|
+
def enable(opts = {})
|
56
|
+
if opts[:source]
|
57
|
+
opts[:server] = opts[:source].server
|
58
|
+
opts[:type] = opts[:source].type
|
59
|
+
end
|
60
|
+
|
61
|
+
send({
|
62
|
+
cmd: 'enable',
|
63
|
+
server: opts[:server],
|
64
|
+
type: opts[:type],
|
65
|
+
})
|
66
|
+
end
|
67
|
+
|
68
|
+
def enable_type(type)
|
69
|
+
@available.each do |source|
|
70
|
+
enable source: source
|
71
|
+
end
|
72
|
+
@enabled_types << type
|
73
|
+
end
|
74
|
+
|
75
|
+
def disable(opts = {})
|
76
|
+
if opts[:source]
|
77
|
+
opts[:server] = opts[:source].server
|
78
|
+
opts[:type] = opts[:source].type
|
79
|
+
end
|
80
|
+
|
81
|
+
send({
|
82
|
+
cmd: 'disable',
|
83
|
+
server: opts[:server],
|
84
|
+
type: opts[:type],
|
85
|
+
})
|
86
|
+
end
|
87
|
+
|
88
|
+
def disable_type(type)
|
89
|
+
@available.each do |source|
|
90
|
+
disable source: source
|
91
|
+
end
|
92
|
+
@enabled_types.delete type
|
93
|
+
end
|
94
|
+
|
95
|
+
def each_log
|
96
|
+
update
|
97
|
+
until @logs.empty?
|
98
|
+
yield @logs.shift
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
def send(message)
|
105
|
+
message = message.to_json
|
106
|
+
STDERR.puts "-> #{message}" if @debug
|
107
|
+
frame = ::WebSocket::Frame::Outgoing::Client.new(version: @handshake.version, type: 'text', data: message)
|
108
|
+
@socket.print frame.to_s
|
109
|
+
end
|
110
|
+
|
111
|
+
def update
|
112
|
+
# Read as much as possible
|
113
|
+
@incoming << @socket.read_nonblock(RECV_LENGTH) while true
|
114
|
+
rescue ::IO::WaitReadable
|
115
|
+
loop do
|
116
|
+
msg = @incoming.next
|
117
|
+
break unless msg
|
118
|
+
STDERR.puts "<- #{msg}" if @debug
|
119
|
+
|
120
|
+
msg = JSON.parse(msg.data)
|
121
|
+
case msg['cmd']
|
122
|
+
when 'connected'
|
123
|
+
@remote_server = msg['server'] if msg['server'].start_with? 'logstream-api'
|
124
|
+
when 'success'
|
125
|
+
# Congratulations!
|
126
|
+
when 'error'
|
127
|
+
raise StreamerRemoteError, "#{msg['code']}: #{msg['description']} during #{msg['during'].inspect}"
|
128
|
+
when 'available'
|
129
|
+
source = Source.new(msg['server'], msg['type'], msg['display_type'])
|
130
|
+
@available << source
|
131
|
+
enable source: source if @enabled_types.include? source.type
|
132
|
+
when 'line'
|
133
|
+
@logs << msg
|
134
|
+
else
|
135
|
+
if @debug
|
136
|
+
STDERR.puts "Received unknown command: #{msg['cmd']}"
|
137
|
+
STDERR.puts msg.inspect
|
138
|
+
else
|
139
|
+
raise StreamerUnrecognisedCommandError, "Unrecognised command: #{msg['cmd']}\n#{msg.inspect}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'acquia/cloud/entity'
|
2
|
+
|
3
|
+
module Acquia
|
4
|
+
class Cloud
|
5
|
+
class Server < Entity
|
6
|
+
def initialize(cloud, site, env, server)
|
7
|
+
@cloud = cloud
|
8
|
+
@site = site
|
9
|
+
@env = env
|
10
|
+
@data = server
|
11
|
+
@url = "/sites/#{@site}/envs/#{env}/servers/#{@data['name']}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
data['name']
|
16
|
+
end
|
17
|
+
|
18
|
+
def fqdn
|
19
|
+
data['fqdn']
|
20
|
+
end
|
21
|
+
|
22
|
+
def ami_type
|
23
|
+
data['ami_type']
|
24
|
+
end
|
25
|
+
|
26
|
+
def ec2_region
|
27
|
+
data['ec2_region']
|
28
|
+
end
|
29
|
+
|
30
|
+
def ec2_availability_zone
|
31
|
+
data['ec2_availability_zone']
|
32
|
+
end
|
33
|
+
|
34
|
+
def services
|
35
|
+
data['services']
|
36
|
+
end
|
37
|
+
|
38
|
+
def varnish?
|
39
|
+
services.has_key? 'varnish'
|
40
|
+
end
|
41
|
+
|
42
|
+
def web?
|
43
|
+
services.has_key? 'web'
|
44
|
+
end
|
45
|
+
|
46
|
+
def database?
|
47
|
+
services.has_key? 'database'
|
48
|
+
end
|
49
|
+
|
50
|
+
def php_procs(memory_limit, apc_shm)
|
51
|
+
return nil unless web?
|
52
|
+
|
53
|
+
values = @cloud.api.get("#{url}/php-procs") do |response|
|
54
|
+
response.params['memory_limits[]'] = memory_limit
|
55
|
+
response.params['apc_shm[]'] = apc_shm
|
56
|
+
end
|
57
|
+
|
58
|
+
values['memory_limit'][memory_limit]['apc_shm'][apc_shm]['php_procs']
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'acquia/cloud/entity'
|
2
|
+
|
3
|
+
module Acquia
|
4
|
+
class Cloud
|
5
|
+
class Site < Entity
|
6
|
+
attr_reader :name
|
7
|
+
|
8
|
+
def initialize(cloud, name)
|
9
|
+
@cloud = cloud
|
10
|
+
@name = name
|
11
|
+
@url = "/sites/#{name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
data['name']
|
16
|
+
end
|
17
|
+
|
18
|
+
def vcs_type
|
19
|
+
data['vcs_type'].to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
def vcs_url
|
23
|
+
data['vcs_url']
|
24
|
+
end
|
25
|
+
|
26
|
+
def production_mode
|
27
|
+
data['production_mode'].to_i != 0
|
28
|
+
end
|
29
|
+
|
30
|
+
def unix_username
|
31
|
+
data['unix_username']
|
32
|
+
end
|
33
|
+
|
34
|
+
def title
|
35
|
+
data['title']
|
36
|
+
end
|
37
|
+
|
38
|
+
def uuid
|
39
|
+
data['uuid']
|
40
|
+
end
|
41
|
+
|
42
|
+
def environments
|
43
|
+
@cloud.api.get("#{@url}/envs").map do |environment|
|
44
|
+
Environment.new(@cloud, @name, environment)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def environment(name)
|
49
|
+
Environment.new(@cloud, @name, {'name' => name}).refresh
|
50
|
+
end
|
51
|
+
|
52
|
+
def databases
|
53
|
+
@cloud.api.get("#{@url}/dbs").map do |db|
|
54
|
+
Database.new(@cloud, @name, db)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def database(name)
|
59
|
+
Database.new(@cloud, @name, {'name' => name}).refresh
|
60
|
+
end
|
61
|
+
|
62
|
+
def tasks
|
63
|
+
@cloud.api.get("#{@url}/tasks").map do |task|
|
64
|
+
Task.new(@cloud, @name, task)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def task(id)
|
69
|
+
Task.new(@cloud, @name, {'id' => id}).refresh
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'acquia/cloud/entity'
|
2
|
+
|
3
|
+
module Acquia
|
4
|
+
class Cloud
|
5
|
+
class Task < Entity
|
6
|
+
def initialize(cloud, site, task)
|
7
|
+
@cloud = cloud
|
8
|
+
@site = site
|
9
|
+
@data = task
|
10
|
+
@url = "/sites/#{@site}/tasks/#{@data['id']}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def id
|
14
|
+
data['id']
|
15
|
+
end
|
16
|
+
|
17
|
+
def queue
|
18
|
+
data['queue']
|
19
|
+
end
|
20
|
+
|
21
|
+
def state
|
22
|
+
data['state'].to_sym
|
23
|
+
end
|
24
|
+
|
25
|
+
def description
|
26
|
+
data['description']
|
27
|
+
end
|
28
|
+
|
29
|
+
def created
|
30
|
+
Time.at(data['created'].to_i)
|
31
|
+
end
|
32
|
+
|
33
|
+
def started
|
34
|
+
Time.at(data['started'].to_i)
|
35
|
+
end
|
36
|
+
|
37
|
+
def completed
|
38
|
+
Time.at(data['completed'].to_i)
|
39
|
+
end
|
40
|
+
|
41
|
+
def logs
|
42
|
+
data['logs']
|
43
|
+
end
|
44
|
+
|
45
|
+
def wait_for_completion(message = nil)
|
46
|
+
print message if message
|
47
|
+
loop do
|
48
|
+
# This will take several minutes, don't spam Acquia too hard while waiting.
|
49
|
+
sleep 15
|
50
|
+
print '.' if message
|
51
|
+
refresh
|
52
|
+
unless [:started].include? state
|
53
|
+
puts if message
|
54
|
+
break
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acquia-cloud
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Equiem
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: websocket
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.3'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.3'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mutant
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.8'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.8'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: mutant-rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.8'
|
97
|
+
description: ''
|
98
|
+
email:
|
99
|
+
- sysadmin@equiem.com.au
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".ruby-version"
|
106
|
+
- ".travis.yml"
|
107
|
+
- Gemfile
|
108
|
+
- Gemfile.lock
|
109
|
+
- LICENSE
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- acquia-cloud.gemspec
|
113
|
+
- lib/acquia/cloud.rb
|
114
|
+
- lib/acquia/cloud/api.rb
|
115
|
+
- lib/acquia/cloud/database.rb
|
116
|
+
- lib/acquia/cloud/entity.rb
|
117
|
+
- lib/acquia/cloud/environment.rb
|
118
|
+
- lib/acquia/cloud/errors.rb
|
119
|
+
- lib/acquia/cloud/logs/source.rb
|
120
|
+
- lib/acquia/cloud/logs/streamer.rb
|
121
|
+
- lib/acquia/cloud/server.rb
|
122
|
+
- lib/acquia/cloud/site.rb
|
123
|
+
- lib/acquia/cloud/task.rb
|
124
|
+
- lib/acquia/cloud/version.rb
|
125
|
+
homepage: https://github.com/Equiem/BOTS
|
126
|
+
licenses:
|
127
|
+
- MIT
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - "~>"
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '2.1'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ">="
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.4.6
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: Bindings to the Acquia Cloud API.
|
149
|
+
test_files: []
|