ohboshwillitfit 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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +39 -0
- data/Rakefile +1 -0
- data/lib/bosh/cli/commands/will_it_fit.rb +67 -0
- data/lib/ohboshwillitfit/fog_credentials.rb +10 -0
- data/lib/ohboshwillitfit/limits.rb +58 -0
- data/lib/ohboshwillitfit/resource.rb +60 -0
- data/lib/ohboshwillitfit/version.rb +3 -0
- data/lib/ohboshwillitfit.rb +6 -0
- data/ohboshwillitfit.gemspec +30 -0
- data/spec/assets/deployment-small.yml +6 -0
- data/spec/assets/deployment-wontfit.yml +6 -0
- data/spec/assets/fog_limits_no_totals.json +68 -0
- data/spec/assets/fog_limits_totals.json +26 -0
- data/spec/fog_credentials_spec.rb +21 -0
- data/spec/limits_spec.rb +82 -0
- data/spec/resource_spec.rb +59 -0
- data/spec/spec_helper.rb +63 -0
- data/spec/support/rspec-fire.rb +9 -0
- metadata +134 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a73b0603f734bf9b868daf32fb7e95e9de171dd
|
4
|
+
data.tar.gz: 44b62bc8889bb050b3539e690dd82dd9b34db8e4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9bd591dd66ba07790f6d91259de62ab4f3ee8f3319b957ee0705b722f5bf58c9a78611c296ee823d207da7ee4b9dbb99a59ad1fbf94f267ef84ed4819e0e0bbf
|
7
|
+
data.tar.gz: e6ac405fbf24ad26aa9bfceeaf81c765bd6c1e0999ce139c47e2dbacd95fc79345c0ca15592da6d8e6d9845a0df13337897f95c60f95a89933325a43a764bb11
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Dr Nic Williams
|
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,39 @@
|
|
1
|
+
# Oh BOSH, Will It Fit?
|
2
|
+
|
3
|
+
Find out early if your target deployment will fit into the quota/available limits of your target OpenStack tenancy.
|
4
|
+
|
5
|
+
If the deployment fits, then you'll see something like:
|
6
|
+
|
7
|
+

|
8
|
+
|
9
|
+
If the deployment won't fit, then you'll see:
|
10
|
+
|
11
|
+

|
12
|
+
|
13
|
+
If the deployment has an invalid flavor/instance type:
|
14
|
+
|
15
|
+

|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
This BOSH CLI plugin is distributed as a RubyGem:
|
20
|
+
|
21
|
+
```
|
22
|
+
gem install ohboshwillitfit
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
Currently, you must have your target OpenStack's credentials stored in a `~/.fog` file and reference the key within that file:
|
28
|
+
|
29
|
+
```
|
30
|
+
bosh will it fit --fog-key default
|
31
|
+
```
|
32
|
+
|
33
|
+
## Contributing
|
34
|
+
|
35
|
+
1. Fork it ( http://github.com/<my-github-username>/ohboshwillitfit/fork )
|
36
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
37
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
38
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
39
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "ohboshwillitfit"
|
2
|
+
|
3
|
+
module Bosh::Cli::Command
|
4
|
+
class WillItFit < Base
|
5
|
+
|
6
|
+
usage "will it fit"
|
7
|
+
desc "check if this deployment will fit into OpenStack tenancy"
|
8
|
+
option "--fog-key default", "Look up credentials in ~/.fog"
|
9
|
+
def will_it_fit
|
10
|
+
deployment_required
|
11
|
+
|
12
|
+
if fog_key = options[:fog_key]
|
13
|
+
credentials = OhBoshWillItFit::FogCredentials.load_from_file(fog_key)
|
14
|
+
end
|
15
|
+
fog_compute = Fog::Compute.new({provider: 'OpenStack'}.merge(credentials))
|
16
|
+
limits = OhBoshWillItFit::Limits.new(fog_compute)
|
17
|
+
unless limits.limits_available?
|
18
|
+
say "Older OpenStacks like this do not provide current resources being used.".make_yellow
|
19
|
+
say "Can only display output based on quotas, rather than unused limits."
|
20
|
+
end
|
21
|
+
|
22
|
+
flavors = fog_compute.flavors
|
23
|
+
flavor_errors = false
|
24
|
+
|
25
|
+
resources = OhBoshWillItFit::Resource.from_file(deployment)
|
26
|
+
OhBoshWillItFit::Resource.map_flavors!(resources, flavors)
|
27
|
+
|
28
|
+
say ""
|
29
|
+
say "Flavours used:"
|
30
|
+
resources.each do |resource|
|
31
|
+
if resource.error
|
32
|
+
say " #{resource.instance_type}: #{resource.error}".make_red
|
33
|
+
flavor_errors = true
|
34
|
+
else
|
35
|
+
say " #{resource.instance_type}: #{resource.size} (ram: #{resource.ram} disk: #{resource.disk} cpus: #{resource.cpus})"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
say ""
|
40
|
+
if flavor_errors
|
41
|
+
say "Available flavors:".make_yellow
|
42
|
+
flavors.sort {|f1, f2| f1.ram <=> f2.ram}.each do |flavor|
|
43
|
+
say " #{flavor.name}: ram: #{flavor.ram} disk: #{flavor.disk} cpus: #{flavor.vcpus}"
|
44
|
+
end
|
45
|
+
else
|
46
|
+
say "Resources used:"
|
47
|
+
resource_totals = OhBoshWillItFit::Resource.resource_totals(resources)
|
48
|
+
display_resource "ram", resource_totals["ram"], limits.ram_size_available
|
49
|
+
display_resource "disk", resource_totals["disk"]
|
50
|
+
display_resource "cpus", resource_totals["cpus"], limits.cores_available
|
51
|
+
end
|
52
|
+
rescue => e
|
53
|
+
err e.message
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def display_resource(label, total, max_total=nil)
|
58
|
+
if max_total
|
59
|
+
message = " #{label}: #{total} / #{max_total}"
|
60
|
+
message = total <= max_total ? message.make_green : message.make_red
|
61
|
+
else
|
62
|
+
message = " #{label}: #{total}"
|
63
|
+
end
|
64
|
+
say message
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module OhBoshWillItFit
|
2
|
+
class FogCredentials
|
3
|
+
def self.load_from_file(fog_key, fog_file="~/.fog")
|
4
|
+
fog_file = File.expand_path(fog_file)
|
5
|
+
return nil unless File.exists?(fog_file)
|
6
|
+
credentials = YAML.load_file(fog_file)
|
7
|
+
credentials[fog_key.to_sym] || credentials[fog_key.to_s]
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module OhBoshWillItFit
|
2
|
+
class Limits
|
3
|
+
attr_reader :fog_compute
|
4
|
+
|
5
|
+
def initialize(fog_compute)
|
6
|
+
@fog_compute = fog_compute
|
7
|
+
end
|
8
|
+
|
9
|
+
def max_total_cores
|
10
|
+
absolute_limits["maxTotalCores"]
|
11
|
+
end
|
12
|
+
|
13
|
+
def max_total_instances
|
14
|
+
absolute_limits["maxTotalInstances"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def max_total_ram_size
|
18
|
+
absolute_limits["maxTotalRAMSize"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def total_cores_used
|
22
|
+
absolute_limits["totalCoresUsed"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def total_instances_used
|
26
|
+
absolute_limits["totalInstancesUsed"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def total_ram_size_used
|
30
|
+
absolute_limits["totalRAMUsed"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def limits_available?
|
34
|
+
total_cores_used && total_instances_used && total_ram_size_used
|
35
|
+
end
|
36
|
+
|
37
|
+
def cores_available
|
38
|
+
max_total_cores - (total_cores_used || 0)
|
39
|
+
end
|
40
|
+
|
41
|
+
def instances_available
|
42
|
+
max_total_instances - (total_instances_used || 0)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ram_size_available
|
46
|
+
max_total_ram_size - (total_ram_size_used || 0)
|
47
|
+
end
|
48
|
+
|
49
|
+
def limits
|
50
|
+
@limits ||= fog_compute.get_limits
|
51
|
+
end
|
52
|
+
|
53
|
+
def absolute_limits
|
54
|
+
limits.data[:body]["limits"]["absolute"]
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module OhBoshWillItFit
|
2
|
+
class Resource
|
3
|
+
attr_accessor :instance_type
|
4
|
+
attr_accessor :size
|
5
|
+
|
6
|
+
attr_accessor :ram, :total_ram
|
7
|
+
attr_accessor :disk, :total_disk
|
8
|
+
attr_accessor :cpus, :total_cpus
|
9
|
+
|
10
|
+
attr_accessor :error
|
11
|
+
|
12
|
+
def self.from_file(deployment_path)
|
13
|
+
resource_pools = YAML.load_file(deployment_path)["resource_pools"]
|
14
|
+
resource_pools.map do |pool|
|
15
|
+
resource = self.new
|
16
|
+
resource.size = pool["size"]
|
17
|
+
resource.instance_type = pool["cloud_properties"]["instance_type"]
|
18
|
+
resource
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.map_flavors!(resources, flavors)
|
23
|
+
resources.each do |resource|
|
24
|
+
if flavor = resource.find_flavor(flavors)
|
25
|
+
resource.apply_flavor(flavor)
|
26
|
+
else
|
27
|
+
resource.error = "Not a valid flavor/instance_type"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.resource_totals(resources)
|
33
|
+
totals = { "ram" => 0, "disk" => 0, "cpus" => 0 }
|
34
|
+
resources.each do |resource|
|
35
|
+
totals["ram"] += resource.total_ram if resource.total_ram
|
36
|
+
totals["disk"] += resource.total_disk if resource.total_disk
|
37
|
+
totals["cpus"] += resource.total_cpus if resource.total_cpus
|
38
|
+
end
|
39
|
+
totals
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_flavor(flavors)
|
43
|
+
flavors.find { |flavor| flavor.name == instance_type }
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply_flavor(flavor)
|
47
|
+
self.ram = flavor.ram
|
48
|
+
self.disk = flavor.disk
|
49
|
+
self.cpus = flavor.vcpus
|
50
|
+
update_totals!
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_totals!
|
54
|
+
self.total_ram = size * ram
|
55
|
+
self.total_disk = size * disk
|
56
|
+
self.total_cpus = size * cpus
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ohboshwillitfit/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ohboshwillitfit"
|
8
|
+
spec.version = OhBoshWillItFit::VERSION
|
9
|
+
spec.authors = ["Dr Nic Williams"]
|
10
|
+
spec.email = ["drnicwilliams@gmail.com"]
|
11
|
+
spec.summary = %q{Will this OpenStack BOSH deployment fit or not? If not, fail fast.}
|
12
|
+
spec.description = <<-EOS
|
13
|
+
Lives have been wasted waiting for a BOSH deployment to run only to find that you
|
14
|
+
don't have a quota large enough. Not enough instances, CPU, RAM, disk or volumes. OMG.
|
15
|
+
Run this BOSH CLI plugin first to find out if it will fit. Fail fast.
|
16
|
+
EOS
|
17
|
+
spec.homepage = ""
|
18
|
+
spec.license = "MIT"
|
19
|
+
|
20
|
+
spec.files = `git ls-files -z`.split("\x0")
|
21
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
22
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_dependency "fog", "~> 1.0"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rspec-fire"
|
30
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
{
|
2
|
+
"limits": {
|
3
|
+
"rate": [
|
4
|
+
{
|
5
|
+
"regex": ".*",
|
6
|
+
"limit": [
|
7
|
+
{
|
8
|
+
"next-available": "2014-03-17T19:30:42Z",
|
9
|
+
"unit": "MINUTE",
|
10
|
+
"verb": "POST",
|
11
|
+
"remaining": 9,
|
12
|
+
"value": 10
|
13
|
+
},
|
14
|
+
{
|
15
|
+
"next-available": "2014-03-20T00:56:41Z",
|
16
|
+
"unit": "MINUTE",
|
17
|
+
"verb": "PUT",
|
18
|
+
"remaining": 10,
|
19
|
+
"value": 10
|
20
|
+
},
|
21
|
+
{
|
22
|
+
"next-available": "2014-03-18T03:47:22Z",
|
23
|
+
"unit": "MINUTE",
|
24
|
+
"verb": "DELETE",
|
25
|
+
"remaining": 99,
|
26
|
+
"value": 100
|
27
|
+
}
|
28
|
+
],
|
29
|
+
"uri": "*"
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"regex": "^/servers",
|
33
|
+
"limit": [
|
34
|
+
{
|
35
|
+
"next-available": "2014-03-20T00:56:41Z",
|
36
|
+
"unit": "DAY",
|
37
|
+
"verb": "POST",
|
38
|
+
"remaining": 50,
|
39
|
+
"value": 50
|
40
|
+
}
|
41
|
+
],
|
42
|
+
"uri": "*/servers"
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"regex": ".*changes-since.*",
|
46
|
+
"limit": [
|
47
|
+
{
|
48
|
+
"next-available": "2014-03-20T00:56:41Z",
|
49
|
+
"unit": "MINUTE",
|
50
|
+
"verb": "GET",
|
51
|
+
"remaining": 3,
|
52
|
+
"value": 3
|
53
|
+
}
|
54
|
+
],
|
55
|
+
"uri": "*changes-since*"
|
56
|
+
}
|
57
|
+
],
|
58
|
+
"absolute": {
|
59
|
+
"maxServerMeta": 128,
|
60
|
+
"maxPersonality": 5,
|
61
|
+
"maxImageMeta": 128,
|
62
|
+
"maxPersonalitySize": 10240,
|
63
|
+
"maxTotalCores": 1000,
|
64
|
+
"maxTotalInstances": 100,
|
65
|
+
"maxTotalRAMSize": 1966080
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
{
|
2
|
+
"limits": {
|
3
|
+
"rate": [
|
4
|
+
|
5
|
+
],
|
6
|
+
"absolute": {
|
7
|
+
"maxServerMeta": 128,
|
8
|
+
"maxPersonality": 5,
|
9
|
+
"maxImageMeta": 128,
|
10
|
+
"maxPersonalitySize": 10240,
|
11
|
+
"maxSecurityGroupRules": 20,
|
12
|
+
"maxTotalKeypairs": 100,
|
13
|
+
"maxSecurityGroups": 10,
|
14
|
+
"maxTotalCores": 50,
|
15
|
+
"maxTotalFloatingIps": 10,
|
16
|
+
"maxTotalInstances": 40,
|
17
|
+
"maxTotalRAMSize": 204800,
|
18
|
+
|
19
|
+
"totalSecurityGroupsUsed": 0,
|
20
|
+
"totalRAMUsed": 169472,
|
21
|
+
"totalInstancesUsed": 24,
|
22
|
+
"totalFloatingIpsUsed": 0,
|
23
|
+
"totalCoresUsed": 43
|
24
|
+
}
|
25
|
+
}
|
26
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
describe OhBoshWillItFit::FogCredentials do
|
2
|
+
it "load_from_file for valid key" do
|
3
|
+
setup_fog_with_various_accounts_setup
|
4
|
+
expected = {
|
5
|
+
openstack_username: "USERNAME",
|
6
|
+
openstack_api_key: "PASSWORD",
|
7
|
+
openstack_tenant: "TENANT",
|
8
|
+
openstack_auth_url: "URL"
|
9
|
+
}
|
10
|
+
expect(OhBoshWillItFit::FogCredentials.load_from_file("default")).to eq(expected)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "load_from_file for unknown key" do
|
14
|
+
setup_fog_with_various_accounts_setup
|
15
|
+
expect(OhBoshWillItFit::FogCredentials.load_from_file("xxx")).to be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it ".fog doesn't exist" do
|
19
|
+
expect(OhBoshWillItFit::FogCredentials.load_from_file("default")).to be_nil
|
20
|
+
end
|
21
|
+
end
|
data/spec/limits_spec.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
describe OhBoshWillItFit::Limits do
|
4
|
+
context "fog_limits - no_totals" do
|
5
|
+
let(:fog_limits) { JSON.parse(File.read(spec_asset("fog_limits_no_totals.json"))) }
|
6
|
+
let(:get_limits) { instance_double("Excon::Response", data: { body: fog_limits} ) }
|
7
|
+
let(:fog_compute) { instance_double("Fog::Compute::OpenStack::Real", get_limits: get_limits) }
|
8
|
+
subject { OhBoshWillItFit::Limits.new(fog_compute) }
|
9
|
+
it {
|
10
|
+
expect(subject.max_total_cores).to eq(1000)
|
11
|
+
}
|
12
|
+
it {
|
13
|
+
expect(subject.max_total_instances).to eq(100)
|
14
|
+
}
|
15
|
+
it {
|
16
|
+
expect(subject.max_total_ram_size).to eq(1966080)
|
17
|
+
}
|
18
|
+
it {
|
19
|
+
expect(subject.total_cores_used).to eq(nil)
|
20
|
+
}
|
21
|
+
it {
|
22
|
+
expect(subject.total_instances_used).to eq(nil)
|
23
|
+
}
|
24
|
+
it {
|
25
|
+
expect(subject.total_ram_size_used).to eq(nil)
|
26
|
+
}
|
27
|
+
|
28
|
+
it {
|
29
|
+
expect(subject.limits_available?).to be_false
|
30
|
+
}
|
31
|
+
|
32
|
+
it {
|
33
|
+
expect(subject.cores_available).to eq(1000)
|
34
|
+
}
|
35
|
+
it {
|
36
|
+
expect(subject.instances_available).to eq(100)
|
37
|
+
}
|
38
|
+
it {
|
39
|
+
expect(subject.ram_size_available).to eq(1966080)
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
context "fog_limits - totals" do
|
44
|
+
let(:fog_limits) { JSON.parse(File.read(spec_asset("fog_limits_totals.json"))) }
|
45
|
+
let(:get_limits) { instance_double("Excon::Response", data: { body: fog_limits} ) }
|
46
|
+
let(:fog_compute) { instance_double("Fog::Compute::OpenStack::Real", get_limits: get_limits) }
|
47
|
+
subject { OhBoshWillItFit::Limits.new(fog_compute) }
|
48
|
+
it {
|
49
|
+
expect(subject.max_total_cores).to eq(50)
|
50
|
+
}
|
51
|
+
it {
|
52
|
+
expect(subject.max_total_instances).to eq(40)
|
53
|
+
}
|
54
|
+
it {
|
55
|
+
expect(subject.max_total_ram_size).to eq(204800)
|
56
|
+
}
|
57
|
+
it {
|
58
|
+
expect(subject.total_cores_used).to eq(43)
|
59
|
+
}
|
60
|
+
it {
|
61
|
+
expect(subject.total_instances_used).to eq(24)
|
62
|
+
}
|
63
|
+
it {
|
64
|
+
expect(subject.total_ram_size_used).to eq(169472)
|
65
|
+
}
|
66
|
+
|
67
|
+
it {
|
68
|
+
expect(subject.limits_available?).to be_true
|
69
|
+
}
|
70
|
+
|
71
|
+
it {
|
72
|
+
expect(subject.cores_available).to eq(50-43)
|
73
|
+
}
|
74
|
+
it {
|
75
|
+
expect(subject.instances_available).to eq(40-24)
|
76
|
+
}
|
77
|
+
it {
|
78
|
+
expect(subject.ram_size_available).to eq(204800-169472)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "fog/openstack/models/compute/flavor"
|
2
|
+
|
3
|
+
describe OhBoshWillItFit::Resource do
|
4
|
+
let(:flavors) do
|
5
|
+
[
|
6
|
+
instance_double("Fog::Compute::OpenStack::Flavor", name: "m1.small", ram: 2048, disk: 12, vcpus: 1),
|
7
|
+
instance_double("Fog::Compute::OpenStack::Flavor", name: "m1.xlarge", ram: 16384, disk: 80, vcpus: 8)
|
8
|
+
]
|
9
|
+
end
|
10
|
+
|
11
|
+
context "small deployment that will fit" do
|
12
|
+
let(:deployment_file) { spec_asset("deployment-small.yml") }
|
13
|
+
subject { OhBoshWillItFit::Resource.from_file(deployment_file) }
|
14
|
+
it { expect(subject.size).to eq(1) }
|
15
|
+
it { expect(subject.first.instance_type).to eq("m1.small") }
|
16
|
+
it { expect(subject.first.size).to eq(2) }
|
17
|
+
|
18
|
+
context "with flavors" do
|
19
|
+
before do
|
20
|
+
OhBoshWillItFit::Resource.map_flavors!(subject, flavors)
|
21
|
+
end
|
22
|
+
it { expect(subject.first.ram).to eq(2048) }
|
23
|
+
it { expect(subject.first.total_ram).to eq(4096) }
|
24
|
+
it { expect(subject.first.disk).to eq(12) }
|
25
|
+
it { expect(subject.first.total_disk).to eq(24) }
|
26
|
+
it { expect(subject.first.cpus).to eq(1) }
|
27
|
+
it { expect(subject.first.total_cpus).to eq(2) }
|
28
|
+
it {
|
29
|
+
totals = OhBoshWillItFit::Resource.resource_totals(subject)
|
30
|
+
expect(totals).to eq({"ram" => 4096, "disk" => 24, "cpus" => 2})
|
31
|
+
}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "small deployment that won't fit" do
|
36
|
+
let(:deployment_file) { spec_asset("deployment-wontfit.yml") }
|
37
|
+
subject { OhBoshWillItFit::Resource.from_file(deployment_file) }
|
38
|
+
it { expect(subject.size).to eq(1) }
|
39
|
+
it { expect(subject.first.instance_type).to eq("m1.xlarge") }
|
40
|
+
it { expect(subject.first.size).to eq(50) }
|
41
|
+
|
42
|
+
context "with flavors" do
|
43
|
+
before do
|
44
|
+
OhBoshWillItFit::Resource.map_flavors!(subject, flavors)
|
45
|
+
end
|
46
|
+
it { expect(subject.first.ram).to eq(16384) }
|
47
|
+
it { expect(subject.first.total_ram).to eq(50 * 16384) }
|
48
|
+
it { expect(subject.first.disk).to eq(80) }
|
49
|
+
it { expect(subject.first.total_disk).to eq(50 * 80) }
|
50
|
+
it { expect(subject.first.cpus).to eq(8) }
|
51
|
+
it { expect(subject.first.total_cpus).to eq(50 * 8) }
|
52
|
+
it {
|
53
|
+
totals = OhBoshWillItFit::Resource.resource_totals(subject)
|
54
|
+
expect(totals).to eq({"ram" => 50 * 16384, "disk" => 50 * 80, "cpus" => 50 * 8})
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# Copyright (c) 2012-2013 Stark & Wayne, LLC
|
2
|
+
|
3
|
+
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", __FILE__)
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require "bundler"
|
7
|
+
Bundler.setup(:default, :test)
|
8
|
+
|
9
|
+
$:.unshift(File.expand_path("../../lib", __FILE__))
|
10
|
+
|
11
|
+
require "rspec/core"
|
12
|
+
require "ohboshwillitfit"
|
13
|
+
|
14
|
+
# for the #sh helper
|
15
|
+
require "rake"
|
16
|
+
require "rake/file_utils"
|
17
|
+
|
18
|
+
# load all files in spec/support/* (but not lower down)
|
19
|
+
Dir[File.dirname(__FILE__) + '/support/*'].each do |path|
|
20
|
+
require path unless File.directory?(path)
|
21
|
+
end
|
22
|
+
|
23
|
+
def setup_fog_with_various_accounts_setup
|
24
|
+
File.open(File.expand_path("~/.fog"), "w") do |f|
|
25
|
+
f << {
|
26
|
+
default: {
|
27
|
+
openstack_username: "USERNAME",
|
28
|
+
openstack_api_key: "PASSWORD",
|
29
|
+
openstack_tenant: "TENANT",
|
30
|
+
openstack_auth_url: "URL"
|
31
|
+
},
|
32
|
+
}.to_yaml
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def spec_asset(filename)
|
37
|
+
File.expand_path("../assets/#{filename}", __FILE__)
|
38
|
+
end
|
39
|
+
|
40
|
+
def setup_home_dir
|
41
|
+
home_dir = File.expand_path("../../tmp/home", __FILE__)
|
42
|
+
FileUtils.rm_rf(home_dir)
|
43
|
+
FileUtils.mkdir_p(home_dir)
|
44
|
+
ENV['HOME'] = home_dir
|
45
|
+
end
|
46
|
+
|
47
|
+
def setup_path
|
48
|
+
ENV['PATH'] = File.expand_path("../../bin", __FILE__) + ":" + ENV['PATH']
|
49
|
+
end
|
50
|
+
# returns the file path to a file
|
51
|
+
# in the fake $HOME folder
|
52
|
+
def home_file(*path)
|
53
|
+
File.join(ENV['HOME'], *path)
|
54
|
+
end
|
55
|
+
|
56
|
+
RSpec.configure do |c|
|
57
|
+
c.before do
|
58
|
+
setup_home_dir
|
59
|
+
setup_path
|
60
|
+
end
|
61
|
+
|
62
|
+
c.color_enabled = true
|
63
|
+
end
|
metadata
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ohboshwillitfit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dr Nic Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: fog
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-fire
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: |2
|
70
|
+
Lives have been wasted waiting for a BOSH deployment to run only to find that you
|
71
|
+
don't have a quota large enough. Not enough instances, CPU, RAM, disk or volumes. OMG.
|
72
|
+
Run this BOSH CLI plugin first to find out if it will fit. Fail fast.
|
73
|
+
email:
|
74
|
+
- drnicwilliams@gmail.com
|
75
|
+
executables: []
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- ".gitignore"
|
80
|
+
- ".rspec"
|
81
|
+
- Gemfile
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- lib/bosh/cli/commands/will_it_fit.rb
|
86
|
+
- lib/ohboshwillitfit.rb
|
87
|
+
- lib/ohboshwillitfit/fog_credentials.rb
|
88
|
+
- lib/ohboshwillitfit/limits.rb
|
89
|
+
- lib/ohboshwillitfit/resource.rb
|
90
|
+
- lib/ohboshwillitfit/version.rb
|
91
|
+
- ohboshwillitfit.gemspec
|
92
|
+
- spec/assets/deployment-small.yml
|
93
|
+
- spec/assets/deployment-wontfit.yml
|
94
|
+
- spec/assets/fog_limits_no_totals.json
|
95
|
+
- spec/assets/fog_limits_totals.json
|
96
|
+
- spec/fog_credentials_spec.rb
|
97
|
+
- spec/limits_spec.rb
|
98
|
+
- spec/resource_spec.rb
|
99
|
+
- spec/spec_helper.rb
|
100
|
+
- spec/support/rspec-fire.rb
|
101
|
+
homepage: ''
|
102
|
+
licenses:
|
103
|
+
- MIT
|
104
|
+
metadata: {}
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '0'
|
114
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
requirements: []
|
120
|
+
rubyforge_project:
|
121
|
+
rubygems_version: 2.2.1
|
122
|
+
signing_key:
|
123
|
+
specification_version: 4
|
124
|
+
summary: Will this OpenStack BOSH deployment fit or not? If not, fail fast.
|
125
|
+
test_files:
|
126
|
+
- spec/assets/deployment-small.yml
|
127
|
+
- spec/assets/deployment-wontfit.yml
|
128
|
+
- spec/assets/fog_limits_no_totals.json
|
129
|
+
- spec/assets/fog_limits_totals.json
|
130
|
+
- spec/fog_credentials_spec.rb
|
131
|
+
- spec/limits_spec.rb
|
132
|
+
- spec/resource_spec.rb
|
133
|
+
- spec/spec_helper.rb
|
134
|
+
- spec/support/rspec-fire.rb
|