bosh-verify-manifest 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,30 @@
1
+ require_relative 'bosh-verify-manifest/helpers'
2
+ require_relative 'bosh-verify-manifest/assertions'
3
+ require_relative 'bosh-verify-manifest/infections'
4
+ require_relative 'bosh-verify-manifest/version'
5
+
6
+ module BoshVerifyManifest
7
+ require 'minitest/spec'
8
+
9
+ class Spec < MiniTest::Spec
10
+ include Assertions
11
+ include Infections
12
+
13
+ def manifest_path
14
+ "#{self.class.name.split('::').last}.yml"
15
+ end
16
+
17
+ def manifest
18
+ YAML::load(File.read(manifest_path))
19
+ end
20
+
21
+ end
22
+
23
+ MiniTest::Spec.register_spec_type(/^bosh_manifest::/, BoshVerifyManifest::Spec)
24
+ end
25
+
26
+ module Kernel
27
+ def describe_bosh_manifest(desc, additional_desc = nil, &block)
28
+ describe("bosh_manifest::#{desc}", additional_desc, &block)
29
+ end
30
+ end
@@ -0,0 +1,92 @@
1
+ require 'ipaddr'
2
+ require 'uuidtools'
3
+ require 'yaml'
4
+
5
+ module BoshVerifyManifest
6
+ module Assertions
7
+
8
+ include Helpers
9
+
10
+ def assert_declares_all_resource_pools(manifest)
11
+ undeclared_pools(manifest).each do |pool|
12
+ flunk "The '#{pool}' pool referred to does not exist"
13
+ end
14
+ end
15
+
16
+ def assert_fills_resource_pools(manifest)
17
+ usage = pool_usage(manifest)
18
+ pool_sizes(manifest).each_pair do |pool, instances|
19
+ assert instances == usage[pool],
20
+ "The '#{pool}' pool is not full (size: #{instances}, wanted: #{usage[pool]})"
21
+ end
22
+ end
23
+
24
+ def assert_has_name(manifest)
25
+ assert manifest.key?('name'), 'The manifest does not have a name'
26
+ end
27
+
28
+ def assert_job_addresses_are_appropriate(manifest)
29
+ manifest['jobs'].each do |job|
30
+ job['networks'].select{|n| n['static_ips']}.each do |network|
31
+ ranges = network(manifest, network['name'])['subnets'].map{|s| s['range']}
32
+ assert ranges.any?{|r| addresses_in_range?(network['static_ips'], r)},
33
+ "The job '#{job['name']}' has static_ips that are not valid for the network '#{network['name']}'"
34
+ end
35
+ end
36
+ end
37
+
38
+ def assert_range_includes_gateway(subnet)
39
+ if subnet['gateway']
40
+ assert IPAddr.new(subnet['range']).include?(IPAddr.new(subnet['gateway']))
41
+ end
42
+ end
43
+
44
+ def assert_specifies_director_uuid(manifest)
45
+ assert manifest.key?('director_uuid'), 'The manifest does not specify the UUID of the bosh director'
46
+ begin
47
+ UUIDTools::UUID.parse(manifest['director_uuid'])
48
+ rescue ArgumentError
49
+ flunk 'The bosh director UUID is not a well-formed UUID'
50
+ end
51
+ end
52
+
53
+ def assert_subnet_addresses_in_range(subnet)
54
+ %w{reserved static}.each do |address_type|
55
+ Array(subnet[address_type]).each do |addresses|
56
+ assert addresses_in_range?(addresses, subnet['range']),
57
+ "The subnet #{address_type} addresses are not within the range #{subnet['range']}"
58
+ end
59
+ end
60
+ end
61
+
62
+ def assert_subnet_ranges_do_not_overlap(subnets)
63
+ ranges = subnets.map{|s| s['range']}
64
+ ranges.permutation(2).each do |range_a, range_b|
65
+ refute IPAddr.new(range_a).include?(IPAddr.new(range_b)),
66
+ "The subnet ranges #{[range_a, range_b].sort.join(' and ')} overlap"
67
+ end
68
+ end
69
+
70
+ def assert_subnets_are_consistent(networks)
71
+ networks.each do |network|
72
+ if network['subnets']
73
+ network['subnets'].each do |subnet|
74
+ assert_range_includes_gateway(subnet)
75
+ assert_subnet_addresses_in_range(subnet)
76
+ end
77
+ assert_subnet_ranges_do_not_overlap(network['subnets'])
78
+ end
79
+ end
80
+ end
81
+
82
+ def refute_exceeds_resource_pools(manifest)
83
+ usage = pool_usage(manifest)
84
+ pool_sizes(manifest).each_pair do |pool, instances|
85
+ assert instances >= usage[pool],
86
+ "The '#{pool}' pool is not large enough (size: #{instances}, wanted: #{usage[pool]})"
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ end
@@ -0,0 +1,58 @@
1
+ require 'ipaddr'
2
+
3
+ module BoshVerifyManifest
4
+ module Assertions
5
+ module Helpers
6
+
7
+ def addresses_in_range?(addresses, range)
8
+ unless range.include?('/') and range =~ %r{/[0-9]+$}
9
+ raise ArgumentError, 'Range must be specified in CIDR format.'
10
+ end
11
+ # Can't get the netmask from an IPAddr instance without monkey patching
12
+ # Consider using an alternate library for this functionality
13
+ mask = range.split('/').last.to_i
14
+ if addresses.include?('-')
15
+ first_and_last_addresses = addresses.split('-').map do |address|
16
+ IPAddr.new(address.strip).to_s
17
+ end.uniq
18
+ expanded_range = IPAddr.new(range).to_range.to_a.map{|i| i.to_s}
19
+ (first_and_last_addresses & expanded_range) == first_and_last_addresses
20
+ else
21
+ Array(addresses).all? do |address|
22
+ if address.include?('-')
23
+ addresses_in_range?(address, range)
24
+ else
25
+ IPAddr.new(range).include?(address)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def network(manifest, name)
32
+ manifest['networks'].find{|n| n['name'] == name} || raise(
33
+ "Network '#{name}' not found in the manifest")
34
+ end
35
+
36
+ def pool_names(manifest)
37
+ manifest['resource_pools'].map{|pool| pool['name']}
38
+ end
39
+
40
+ def pool_sizes(manifest)
41
+ Hash[manifest['resource_pools'].map{|pool| [pool['name'], pool['size']]}]
42
+ end
43
+
44
+ def pool_usage(manifest)
45
+ Hash[manifest['jobs'].group_by do |job|
46
+ job['resource_pool']
47
+ end.map do |pool, jobs|
48
+ [pool, jobs.inject(0){|count, job| count += job['instances']}]
49
+ end]
50
+ end
51
+
52
+ def undeclared_pools(manifest)
53
+ pool_usage(manifest).keys.sort - pool_names(manifest).sort
54
+ end
55
+
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,13 @@
1
+ module BoshVerifyManifest
2
+ module Infections
3
+
4
+ Hash.infect_an_assertion :assert_fills_resource_pools, 'must_fill_resource_pools', :only_one_argument
5
+ Hash.infect_an_assertion :assert_has_name, 'must_be_named', :only_one_argument
6
+ Hash.infect_an_assertion :assert_job_addresses_are_appropriate, 'must_have_appropriate_job_addresses', :only_one_argument
7
+ Hash.infect_an_assertion :assert_specifies_director_uuid, 'must_specify_director_uuid', :only_one_argument
8
+ Array.infect_an_assertion :assert_subnets_are_consistent, 'must_have_consistent_subnets', :only_one_argument
9
+ Hash.infect_an_assertion :refute_exceeds_resource_pools, 'wont_exceed_resource_pools', :only_one_argument
10
+ Hash.infect_an_assertion :assert_declares_all_resource_pools, 'must_declare_all_resource_pools', :only_one_argument
11
+
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ module BoshVerifyManifest
2
+ # The current version of bosh-verify-manifest
3
+ VERSION = '0.1.0'
4
+ end
@@ -0,0 +1,33 @@
1
+ require 'minitest/spec'
2
+ require 'bosh-verify-manifest/helpers'
3
+ require 'bosh-verify-manifest/assertions'
4
+
5
+ module Bosh::Cli::Command
6
+ class VerifyManifest < Base
7
+
8
+ include MiniTest::Assertions
9
+ include BoshVerifyManifest::Assertions
10
+
11
+ def manifest
12
+ @manifest ||= YAML::load(File.read(deployment))
13
+ end
14
+
15
+ usage "verify manifest"
16
+ desc "Check the BOSH manifest for common errors"
17
+ def verify_manifest
18
+ deployment_required
19
+ begin
20
+ assert_has_name(manifest)
21
+ assert_specifies_director_uuid(manifest)
22
+ assert_subnets_are_consistent(manifest['networks'])
23
+ assert_job_addresses_are_appropriate(manifest)
24
+ assert_declares_all_resource_pools(manifest)
25
+ refute_exceeds_resource_pools(manifest)
26
+ assert_fills_resource_pools(manifest)
27
+ rescue MiniTest::Assertion => a
28
+ err a.to_s
29
+ end
30
+ end
31
+
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bosh-verify-manifest
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Crump
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bosh_cli
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
+ - !ruby/object:Gem::Dependency
31
+ name: minitest
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 4.7.4
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: 4.7.4
46
+ - !ruby/object:Gem::Dependency
47
+ name: uuidtools
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 2.1.3
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: 2.1.3
62
+ description: Validate BOSH deployment manifests.
63
+ email:
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - lib/bosh-verify-manifest.rb
69
+ - lib/bosh/cli/commands/verify.rb
70
+ - lib/bosh-verify-manifest/infections.rb
71
+ - lib/bosh-verify-manifest/version.rb
72
+ - lib/bosh-verify-manifest/assertions.rb
73
+ - lib/bosh-verify-manifest/helpers.rb
74
+ homepage: http://github.com/cloudcredo/bosh-verify-manifest
75
+ licenses:
76
+ - MIT
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ! '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ segments:
88
+ - 0
89
+ hash: -1852599430210049349
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ segments:
97
+ - 0
98
+ hash: -1852599430210049349
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 1.8.25
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: bosh-verify-manifest-0.1.0
105
+ test_files: []