ey-provisioner 1.0.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.
- data/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +60 -0
- data/Rakefile +2 -0
- data/TODO +2 -0
- data/ey-provisioner.gemspec +29 -0
- data/lib/ey-provisioner.rb +16 -0
- data/lib/ey-provisioner/api_error.rb +39 -0
- data/lib/ey-provisioner/connection.rb +47 -0
- data/lib/ey-provisioner/environment.rb +45 -0
- data/lib/ey-provisioner/instance.rb +41 -0
- data/lib/ey-provisioner/invalid_request.rb +12 -0
- data/lib/ey-provisioner/request.rb +48 -0
- data/lib/ey-provisioner/version.rb +5 -0
- data/spec/api_error_spec.rb +68 -0
- data/spec/connection_spec.rb +37 -0
- data/spec/environment_spec.rb +91 -0
- data/spec/instance_spec.rb +30 -0
- data/spec/request_spec.rb +85 -0
- data/spec/spec_helper.rb +15 -0
- metadata +237 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.3-p448@ey-provisioner --create
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Dan Draper
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Ey::Provisioner
|
2
|
+
|
3
|
+
[](https://travis-ci.org/coderdan/ey-provisioner)
|
4
|
+
[](https://codeclimate.com/github/coderdan/ey-provisioner)
|
5
|
+
|
6
|
+
The EY Provisioner is a Ruby based client gem for interacting with the [EngineYard](http://www.engineyard.com) Instance Provisioning V2 API.
|
7
|
+
See [this blog post](https://support.cloud.engineyard.com/entries/22498973-use-the-instance-provisioning-api-with-engine-yard-cloud "Engine Yard Instance Provisioning")
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
gem 'ey-provisioner'
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install ey-provisioner
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
To use the ey-provisioner start by creating a connection:
|
26
|
+
|
27
|
+
connection = Ey::Provisioner::Connection.new(token)
|
28
|
+
|
29
|
+
Via the connection, you get the relevant environment:
|
30
|
+
|
31
|
+
env = connection.environment(1234)
|
32
|
+
|
33
|
+
On the connection you can now add or remove instances:
|
34
|
+
|
35
|
+
env.add_instance(:name => "my-instance", :role => "util", :instance_size => 'xlarge')
|
36
|
+
env.remove_instances(:role => "util") # Will shutdown all util servers
|
37
|
+
|
38
|
+
### Getting your token
|
39
|
+
|
40
|
+
See [the EY docs](https://support.cloud.engineyard.com/entries/22498973-use-the-instance-provisioning-api-with-engine-yard-cloud "Engine Yard Instance Provisioning") for instructions on how to get your token.
|
41
|
+
|
42
|
+
### Getting the environment ID
|
43
|
+
|
44
|
+
Within the EngineYard interface, click on the deployment history link within the Environment. The page's URL will be of the form .../environments/x/deployments. X will be the environment ID.
|
45
|
+
|
46
|
+
## Limitations
|
47
|
+
|
48
|
+
The Gem does not currently support retrieving provisioning status nor does it allow you to list all environments for the account.
|
49
|
+
|
50
|
+
## Documentation
|
51
|
+
|
52
|
+
Full documentation can be found [here](http://rubydoc.info/github/coderdan/ey-provisioner/master/frames).
|
53
|
+
|
54
|
+
## Contributing
|
55
|
+
|
56
|
+
1. Fork it
|
57
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
58
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
59
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
60
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/ey-provisioner/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Dan Draper"]
|
6
|
+
gem.email = ["daniel@codehire.com"]
|
7
|
+
gem.description = %q{Ruby based client gem for interacting with the EngineYard Instance Provisioning API}
|
8
|
+
gem.summary = %q{Ruby based client gem for interacting with the EngineYard Instance Provisioning API}
|
9
|
+
gem.homepage = "https://github.com/coderdan/ey-provisioner"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "ey-provisioner"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Ey::Provisioner::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'excon', ">= 0.25"
|
19
|
+
gem.add_dependency 'json'
|
20
|
+
gem.add_dependency 'activemodel', '>= 3.2'
|
21
|
+
|
22
|
+
gem.add_development_dependency 'pry'
|
23
|
+
gem.add_development_dependency 'yard'
|
24
|
+
gem.add_development_dependency 'redcarpet'
|
25
|
+
gem.add_development_dependency 'rspec'
|
26
|
+
gem.add_development_dependency 'mocha'
|
27
|
+
gem.add_development_dependency 'shoulda'
|
28
|
+
gem.add_development_dependency 'activesupport'
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'excon'
|
2
|
+
require 'json'
|
3
|
+
require 'active_model'
|
4
|
+
require "ey-provisioner/api_error"
|
5
|
+
require "ey-provisioner/connection"
|
6
|
+
require "ey-provisioner/environment"
|
7
|
+
require "ey-provisioner/instance"
|
8
|
+
require "ey-provisioner/invalid_request"
|
9
|
+
require "ey-provisioner/request"
|
10
|
+
require "ey-provisioner/version"
|
11
|
+
|
12
|
+
module Ey
|
13
|
+
module Provisioner
|
14
|
+
# Your code goes here...
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ey
|
2
|
+
module Provisioner
|
3
|
+
class APIError < RuntimeError
|
4
|
+
attr_reader :errors, :request, :status
|
5
|
+
|
6
|
+
def initialize(error)
|
7
|
+
@error = error
|
8
|
+
parse_error
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
if @status
|
13
|
+
"#{@status}: #{error_message}"
|
14
|
+
else
|
15
|
+
@error.message
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Specific EngineYard API error
|
20
|
+
def error_message
|
21
|
+
if @errors
|
22
|
+
@errors.map { |(k,v)|
|
23
|
+
"#{k} #{v.join(',')}"
|
24
|
+
}.join("; ")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def parse_error
|
30
|
+
if @error.respond_to?(:response) && @error.response.body.present?
|
31
|
+
body_hash = JSON(@error.response.body)
|
32
|
+
@status = body_hash.fetch('status', 'unknown')
|
33
|
+
@errors = body_hash['errors']
|
34
|
+
@request = body_hash['request']
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Ey
|
2
|
+
module Provisioner
|
3
|
+
# Maintains a connection representation (stateless) to the EY API
|
4
|
+
# Supports V2 of the API
|
5
|
+
class Connection
|
6
|
+
EY_API_CLOUD_URL = "https://cloud.engineyard.com:443/"
|
7
|
+
|
8
|
+
# Create a connection
|
9
|
+
# @param token [String] the EY token
|
10
|
+
def initialize(token)
|
11
|
+
@token = token
|
12
|
+
@http = Excon.new(EY_API_CLOUD_URL)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get an environment instance
|
16
|
+
#
|
17
|
+
# @param id [Integer] the environment ID
|
18
|
+
# @return [Environment]
|
19
|
+
def environment(id)
|
20
|
+
Environment.new(self, id)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Perform an API POST to the path
|
24
|
+
#
|
25
|
+
# @param path [String] the partial path (ie; everything after /api/v2/)
|
26
|
+
# @param request [Request] a request object
|
27
|
+
# @return [Excon::Response]
|
28
|
+
def http_post(path, request)
|
29
|
+
@http.post(
|
30
|
+
:body => JSON(:request => request.to_hash),
|
31
|
+
:headers => headers,
|
32
|
+
:path => "/api/v2/#{path}",
|
33
|
+
:expects => [200, 201, 202]
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
def headers
|
39
|
+
{
|
40
|
+
'Accept' => 'application/json',
|
41
|
+
'X-EY-Cloud-Token' => @token,
|
42
|
+
'Content-type' => 'application/json'
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Ey
|
2
|
+
module Provisioner
|
3
|
+
class Environment
|
4
|
+
def initialize(connection, env_id)
|
5
|
+
@connection = connection
|
6
|
+
@env_id = env_id
|
7
|
+
end
|
8
|
+
|
9
|
+
# Remove instances from the environment
|
10
|
+
#
|
11
|
+
# @param options [Hash] attributes to define what to remove (eg; name, role). See EY docs
|
12
|
+
def remove_instances(options = {})
|
13
|
+
process_post(path_for(:remove_instances), options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Add an instance to the environment
|
17
|
+
#
|
18
|
+
# @param options [Hash] attributes to define the server to add (eg; name, role). See EY docs
|
19
|
+
def add_instance(options = {})
|
20
|
+
process_post(path_for(:add_instances), options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
def path_for(which)
|
25
|
+
"environments/#{@env_id}/#{which}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def api_error_handler
|
29
|
+
begin
|
30
|
+
yield if block_given?
|
31
|
+
rescue Excon::Errors::Error => e
|
32
|
+
raise APIError.new(e)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def process_post(path, options)
|
37
|
+
api_error_handler do
|
38
|
+
request = Request.new(options)
|
39
|
+
response = @connection.http_post(path, request)
|
40
|
+
Instance.new(JSON(response.body)['instance'])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Ey
|
2
|
+
module Provisioner
|
3
|
+
# Represents an EY instance
|
4
|
+
class Instance
|
5
|
+
attr_accessor :id
|
6
|
+
attr_accessor :amazon_id
|
7
|
+
attr_accessor :bootstrapped_at
|
8
|
+
attr_accessor :chef_status
|
9
|
+
attr_accessor :name
|
10
|
+
attr_accessor :private_hostname
|
11
|
+
attr_accessor :public_hostname
|
12
|
+
attr_accessor :role
|
13
|
+
attr_accessor :status
|
14
|
+
|
15
|
+
TYPES = %w(
|
16
|
+
small
|
17
|
+
small_64
|
18
|
+
medium_ram
|
19
|
+
medium_ram_64
|
20
|
+
medium_cpu
|
21
|
+
medium_cpu_64
|
22
|
+
large
|
23
|
+
xlarge
|
24
|
+
xlarge_cpu
|
25
|
+
xlarge_ram
|
26
|
+
doublexlarge_ram
|
27
|
+
quadxlarge_ram
|
28
|
+
quadxlarge_io
|
29
|
+
)
|
30
|
+
|
31
|
+
# Create an Instance representation
|
32
|
+
#
|
33
|
+
# @param attrs [Hash]
|
34
|
+
def initialize(attrs)
|
35
|
+
attrs.each do |(attr, value)|
|
36
|
+
send("#{attr}=", value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Ey
|
2
|
+
module Provisioner
|
3
|
+
# Implement the presenter pattern for API requests
|
4
|
+
class Request
|
5
|
+
include ActiveModel::Validations
|
6
|
+
|
7
|
+
attr_accessor :role
|
8
|
+
attr_accessor :name
|
9
|
+
attr_accessor :instance_size
|
10
|
+
attr_accessor :snapshot_id
|
11
|
+
attr_accessor :availability_zone
|
12
|
+
attr_reader :volume_size
|
13
|
+
|
14
|
+
validates :role,
|
15
|
+
:presence => true,
|
16
|
+
:inclusion => { :in => %w(app util) }
|
17
|
+
|
18
|
+
validates :instance_size,
|
19
|
+
:inclusion => { :in => Instance::TYPES },
|
20
|
+
:allow_nil => true
|
21
|
+
|
22
|
+
# Create a new request
|
23
|
+
#
|
24
|
+
# @param options [Hash]
|
25
|
+
def initialize(options = {})
|
26
|
+
options.each do |(attr,value)|
|
27
|
+
send("#{attr}=", value)
|
28
|
+
end
|
29
|
+
self.role ||= 'util'
|
30
|
+
end
|
31
|
+
|
32
|
+
def volume_size=(arg)
|
33
|
+
@volume_size = arg.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
# A hash representation of the request
|
37
|
+
def to_hash
|
38
|
+
raise InvalidRequest.new(self) if !valid?
|
39
|
+
{}.tap do |request|
|
40
|
+
request[:role] = self.role
|
41
|
+
[ :name, :instance_size, :volume_size, :snapshot_id, :availability_zone ].each do |attr|
|
42
|
+
request[attr] = send(attr) unless send(attr).nil?
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ey::Provisioner::APIError do
|
4
|
+
subject { Ey::Provisioner::APIError.new(error) }
|
5
|
+
|
6
|
+
describe "standard error" do
|
7
|
+
let(:error) { stub("error", :message => "message") }
|
8
|
+
its(:message) { should == "message" }
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "an HTTP error with a response" do
|
12
|
+
context "with a state" do
|
13
|
+
let(:body_hash) do
|
14
|
+
{
|
15
|
+
'request' => {"role"=>"util", "volume_size"=>"2000"},
|
16
|
+
'status' => 'rejected',
|
17
|
+
'errors' => errors
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
context "with errors" do
|
22
|
+
let(:errors) { {"name"=>["can't be blank"]} }
|
23
|
+
let(:response) { stub("response", :body => JSON(body_hash)) }
|
24
|
+
let(:error) { stub("error", :message => "message", :response => response) }
|
25
|
+
its(:message) { should == "rejected: name can't be blank" }
|
26
|
+
its(:error_message) { should == "name can't be blank" }
|
27
|
+
its(:errors) { should == errors }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "with no errors" do
|
31
|
+
let(:errors) { nil }
|
32
|
+
let(:response) { stub("response", :body => JSON(body_hash)) }
|
33
|
+
let(:error) { stub("error", :message => "message", :response => response) }
|
34
|
+
its(:message) { should == "rejected: " }
|
35
|
+
its(:error_message) { should be_nil }
|
36
|
+
its(:errors) { should be_nil }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with a state" do
|
41
|
+
let(:body_hash) do
|
42
|
+
{
|
43
|
+
'request' => {"role"=>"util", "volume_size"=>"2000"},
|
44
|
+
'errors' => errors
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
context "with errors" do
|
49
|
+
let(:errors) { {"name"=>["can't be blank"]} }
|
50
|
+
let(:response) { stub("response", :body => JSON(body_hash)) }
|
51
|
+
let(:error) { stub("error", :message => "message", :response => response) }
|
52
|
+
its(:message) { should == "unknown: name can't be blank" }
|
53
|
+
its(:error_message) { "name can't be blank" }
|
54
|
+
its(:errors) { errors }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "with an empty string body" do
|
59
|
+
let(:errors) { nil }
|
60
|
+
let(:response) { stub("response", :body => "") }
|
61
|
+
let(:error) { stub("error", :message => "message", :response => response) }
|
62
|
+
|
63
|
+
its(:message) { should == "message" }
|
64
|
+
its(:error_message) { should be_nil }
|
65
|
+
its(:errors) { should be_nil }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ey::Provisioner::Connection do
|
4
|
+
subject { Ey::Provisioner::Connection.new("tokenabc") }
|
5
|
+
let(:http) { stub('http') }
|
6
|
+
before { Excon.expects(:new).with(Ey::Provisioner::Connection::EY_API_CLOUD_URL).returns(http) }
|
7
|
+
|
8
|
+
let(:headers) do
|
9
|
+
{
|
10
|
+
'Accept' => 'application/json',
|
11
|
+
'X-EY-Cloud-Token' => 'tokenabc',
|
12
|
+
'Content-type' => 'application/json'
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "http_post" do
|
17
|
+
let(:request) { stub('request') }
|
18
|
+
before { request.expects(:to_hash).returns(:test => 'data') }
|
19
|
+
|
20
|
+
it "should post via HTTP to the API" do
|
21
|
+
http.expects(:post).with(
|
22
|
+
:body => JSON(:request => { :test => 'data' }),
|
23
|
+
:headers => headers,
|
24
|
+
:path => "/api/v2/testpath",
|
25
|
+
:expects => [200, 201, 202]
|
26
|
+
)
|
27
|
+
|
28
|
+
subject.http_post("testpath", request)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "environment" do
|
33
|
+
it "should return an environment instance" do
|
34
|
+
subject.environment(1234).should be_a(Ey::Provisioner::Environment)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ey::Provisioner::Environment do
|
4
|
+
before(:all) { Excon.defaults[:mock] = true }
|
5
|
+
let(:connection) { Ey::Provisioner::Connection.new("123456789") }
|
6
|
+
subject { Ey::Provisioner::Environment.new(connection, "1") }
|
7
|
+
|
8
|
+
describe "Add instance" do
|
9
|
+
let(:path) { 'environments/1/add_instances' }
|
10
|
+
|
11
|
+
context "an invalid server response" do
|
12
|
+
before { Excon.stub({:method => :post}, {:status => 500}) }
|
13
|
+
|
14
|
+
it "will throw an error" do
|
15
|
+
expect { subject.add_instance }.to raise_error(Ey::Provisioner::APIError)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "valid server response" do
|
20
|
+
let(:response) { stub('response') }
|
21
|
+
let(:request) { stub('request') }
|
22
|
+
before { response.stubs(:body).returns(JSON('instance' => { 'id' => 1234 })) }
|
23
|
+
|
24
|
+
context "with default options" do
|
25
|
+
before { Ey::Provisioner::Request.expects(:new).with({}).returns(request) }
|
26
|
+
|
27
|
+
it "should post the data" do
|
28
|
+
connection.expects(:http_post).with(path, request).returns(response)
|
29
|
+
subject.add_instance
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return the instance" do
|
33
|
+
connection.expects(:http_post).with(path, request).returns(response)
|
34
|
+
subject.add_instance.should be_a(Ey::Provisioner::Instance)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with custom options" do
|
39
|
+
let(:options) { stub('options') }
|
40
|
+
before { Ey::Provisioner::Request.expects(:new).with(options).returns(request) }
|
41
|
+
|
42
|
+
it "should post the data" do
|
43
|
+
connection.expects(:http_post).with(path, request).returns(response)
|
44
|
+
subject.add_instance(options)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "Remove instances" do
|
51
|
+
let(:path) { 'environments/1/remove_instances' }
|
52
|
+
|
53
|
+
context "an invalid server response" do
|
54
|
+
before { Excon.stub({:method => :post}, {:status => 500}) }
|
55
|
+
|
56
|
+
it "will throw an error" do
|
57
|
+
expect { subject.remove_instances }.to raise_error(Ey::Provisioner::APIError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "valid server response" do
|
62
|
+
let(:response) { stub('response') }
|
63
|
+
let(:request) { stub('request') }
|
64
|
+
before { response.stubs(:body).returns(JSON('instance' => { 'id' => 1234 })) }
|
65
|
+
|
66
|
+
context "with default options" do
|
67
|
+
before { Ey::Provisioner::Request.expects(:new).with({}).returns(request) }
|
68
|
+
|
69
|
+
it "should post the data" do
|
70
|
+
connection.expects(:http_post).with(path, request).returns(response)
|
71
|
+
subject.remove_instances
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should return the instance" do
|
75
|
+
connection.expects(:http_post).with(path, request).returns(response)
|
76
|
+
subject.remove_instances.should be_a(Ey::Provisioner::Instance)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "with custom options" do
|
81
|
+
let(:options) { stub('options') }
|
82
|
+
before { Ey::Provisioner::Request.expects(:new).with(options).returns(request) }
|
83
|
+
|
84
|
+
it "should post the data" do
|
85
|
+
connection.expects(:http_post).with(path, request).returns(response)
|
86
|
+
subject.remove_instances(options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ey::Provisioner::Instance do
|
4
|
+
describe "initialize" do
|
5
|
+
let(:instance_attrs) do
|
6
|
+
{
|
7
|
+
"amazon_id" => "1234",
|
8
|
+
"bootstrapped_at" => "2013-01-01 12:00",
|
9
|
+
"chef_status" => [{"message"=>"Processing memcached", "timestamp"=>"2013-08-09T01:25:25+00:00"}],
|
10
|
+
"id" => 260323,
|
11
|
+
"name" => "dantest",
|
12
|
+
"private_hostname" => "privatehostname",
|
13
|
+
"public_hostname" => "publichostname",
|
14
|
+
"role" => "util",
|
15
|
+
"status" => "starting"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
subject { Ey::Provisioner::Instance.new(instance_attrs) }
|
20
|
+
its(:amazon_id) { should == "1234" }
|
21
|
+
its(:id) { should == 260323 }
|
22
|
+
its(:bootstrapped_at) { should == "2013-01-01 12:00" }
|
23
|
+
its(:chef_status) { [{"message"=>"Processing memcached", "timestamp"=>"2013-08-09T01:25:25+00:00"}] }
|
24
|
+
its(:name) { should == 'dantest' }
|
25
|
+
its(:private_hostname) { should == 'privatehostname' }
|
26
|
+
its(:public_hostname) { should == 'publichostname' }
|
27
|
+
its(:role) { should == 'util' }
|
28
|
+
its(:status) { should == 'starting' }
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Ey::Provisioner::Request do
|
4
|
+
it { should validate_presence_of(:role) }
|
5
|
+
|
6
|
+
it { should allow_value('app').for(:role) }
|
7
|
+
it { should allow_value('util').for(:role) }
|
8
|
+
it { should_not allow_value('db').for(:role) }
|
9
|
+
|
10
|
+
Ey::Provisioner::Instance::TYPES.each do |instance_type|
|
11
|
+
it { should allow_value(instance_type).for(:instance_size) }
|
12
|
+
end
|
13
|
+
it { should_not allow_value('another').for(:instance_size) }
|
14
|
+
it { should allow_value(nil).for(:instance_size) }
|
15
|
+
|
16
|
+
describe "initialize" do
|
17
|
+
context "all available attrs" do
|
18
|
+
let(:request) { Ey::Provisioner::Request.new(:name => "My Name", :role => "app") }
|
19
|
+
subject { request }
|
20
|
+
its(:name) { should == "My Name" }
|
21
|
+
its(:role) { should == "app" }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "an unknown attr" do
|
25
|
+
it "should throw an error" do
|
26
|
+
expect { Ey::Provisioner::Request.new(:foo => "bar") }.to raise_error
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "defaults" do
|
31
|
+
let(:request) { Ey::Provisioner::Request.new }
|
32
|
+
subject { request }
|
33
|
+
its(:role) { should == "util" }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "to_hash" do
|
38
|
+
subject { request.to_hash }
|
39
|
+
|
40
|
+
context "with valid data" do
|
41
|
+
context "with only defaults" do
|
42
|
+
let(:request) { Ey::Provisioner::Request.new }
|
43
|
+
it { should == { :role => 'util' } }
|
44
|
+
end
|
45
|
+
|
46
|
+
context "with a role" do
|
47
|
+
let(:request) { Ey::Provisioner::Request.new(:role => 'app') }
|
48
|
+
it { should == { :role => 'app' } }
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with a name provided" do
|
52
|
+
let(:request) { Ey::Provisioner::Request.new(:name => "My Name") }
|
53
|
+
it { should == { :name => "My Name", :role => 'util' } }
|
54
|
+
end
|
55
|
+
|
56
|
+
context "with an instance size" do
|
57
|
+
let(:request) { Ey::Provisioner::Request.new(:instance_size => "small") }
|
58
|
+
it { should == { :instance_size => "small", :role => 'util' } }
|
59
|
+
end
|
60
|
+
|
61
|
+
context "with a volume_size" do
|
62
|
+
let(:request) { Ey::Provisioner::Request.new(:volume_size => 100) }
|
63
|
+
it { should == { :volume_size => "100", :role => 'util' } }
|
64
|
+
end
|
65
|
+
|
66
|
+
context "with a snapshot_id" do
|
67
|
+
let(:request) { Ey::Provisioner::Request.new(:snapshot_id => "snap-99999999") }
|
68
|
+
it { should == { :snapshot_id => "snap-99999999", :role => 'util' } }
|
69
|
+
end
|
70
|
+
|
71
|
+
context "with a availability_zone" do
|
72
|
+
let(:request) { Ey::Provisioner::Request.new(:availability_zone => "us-east-1a") }
|
73
|
+
it { should == { :availability_zone => "us-east-1a", :role => 'util' } }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with invalid data" do
|
79
|
+
let(:request) { Ey::Provisioner::Request.new(:role => "invalid role") }
|
80
|
+
|
81
|
+
it "should raise an error" do
|
82
|
+
expect { request.to_hash }.to raise_error(Ey::Provisioner::InvalidRequest, "Invalid Request: Role is not included in the list")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.expand_path("../../lib/ey-provisioner", __FILE__)
|
2
|
+
|
3
|
+
Dir[File.expand_path("../../spec/support/**/*.rb", __FILE__)].each {|f| require f}
|
4
|
+
|
5
|
+
require 'active_support'
|
6
|
+
require 'active_support/core_ext/array/wrap'
|
7
|
+
|
8
|
+
require 'pry'
|
9
|
+
require 'mocha/api'
|
10
|
+
require 'shoulda'
|
11
|
+
require 'shoulda/matchers'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.mock_with :mocha
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ey-provisioner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dan Draper
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: excon
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.25'
|
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.25'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: activemodel
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.2'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.2'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: pry
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: yard
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: redcarpet
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: rspec
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: mocha
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: shoulda
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: activesupport
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
description: Ruby based client gem for interacting with the EngineYard Instance Provisioning
|
175
|
+
API
|
176
|
+
email:
|
177
|
+
- daniel@codehire.com
|
178
|
+
executables: []
|
179
|
+
extensions: []
|
180
|
+
extra_rdoc_files: []
|
181
|
+
files:
|
182
|
+
- .gitignore
|
183
|
+
- .rvmrc
|
184
|
+
- .travis.yml
|
185
|
+
- Gemfile
|
186
|
+
- LICENSE
|
187
|
+
- README.md
|
188
|
+
- Rakefile
|
189
|
+
- TODO
|
190
|
+
- ey-provisioner.gemspec
|
191
|
+
- lib/ey-provisioner.rb
|
192
|
+
- lib/ey-provisioner/api_error.rb
|
193
|
+
- lib/ey-provisioner/connection.rb
|
194
|
+
- lib/ey-provisioner/environment.rb
|
195
|
+
- lib/ey-provisioner/instance.rb
|
196
|
+
- lib/ey-provisioner/invalid_request.rb
|
197
|
+
- lib/ey-provisioner/request.rb
|
198
|
+
- lib/ey-provisioner/version.rb
|
199
|
+
- spec/api_error_spec.rb
|
200
|
+
- spec/connection_spec.rb
|
201
|
+
- spec/environment_spec.rb
|
202
|
+
- spec/instance_spec.rb
|
203
|
+
- spec/request_spec.rb
|
204
|
+
- spec/spec_helper.rb
|
205
|
+
homepage: https://github.com/coderdan/ey-provisioner
|
206
|
+
licenses: []
|
207
|
+
post_install_message:
|
208
|
+
rdoc_options: []
|
209
|
+
require_paths:
|
210
|
+
- lib
|
211
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
212
|
+
none: false
|
213
|
+
requirements:
|
214
|
+
- - ! '>='
|
215
|
+
- !ruby/object:Gem::Version
|
216
|
+
version: '0'
|
217
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
218
|
+
none: false
|
219
|
+
requirements:
|
220
|
+
- - ! '>='
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
requirements: []
|
224
|
+
rubyforge_project:
|
225
|
+
rubygems_version: 1.8.25
|
226
|
+
signing_key:
|
227
|
+
specification_version: 3
|
228
|
+
summary: Ruby based client gem for interacting with the EngineYard Instance Provisioning
|
229
|
+
API
|
230
|
+
test_files:
|
231
|
+
- spec/api_error_spec.rb
|
232
|
+
- spec/connection_spec.rb
|
233
|
+
- spec/environment_spec.rb
|
234
|
+
- spec/instance_spec.rb
|
235
|
+
- spec/request_spec.rb
|
236
|
+
- spec/spec_helper.rb
|
237
|
+
has_rdoc:
|