guard-bosh 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ab9503fc851cdf19034ac1606debeacb0f2f085c
4
+ data.tar.gz: 533de1a40a7b769549221eac66627419d1782f41
5
+ SHA512:
6
+ metadata.gz: c4e0d898885f104008bf467c695667211e986e8d787a167bba61f1c0d555ae23e700da025c920c5962620aa219ad5127b94beacc0541054d482c15edc0d38c1b
7
+ data.tar.gz: edabe6e92e3c1cebec6cb08739eca1bb149c512fed9d4b288bc19a34139bb6549cdd6654cd2f006f1691b0fe89b0c6ed9a3221ed59cda6e176cc3c9c2e92fb2e
data/lib/guard/bosh.rb ADDED
@@ -0,0 +1,94 @@
1
+ require 'guard/compat/plugin'
2
+
3
+ module Guard
4
+ class Bosh < Plugin
5
+ require 'pathname'
6
+
7
+ require 'guard/bosh/apply_specification'
8
+ require 'guard/bosh/change_assessor'
9
+ require 'guard/bosh/effective_properties_calculator'
10
+ require 'guard/bosh/global_properties_loader'
11
+ require 'guard/bosh/job_default_properties_loader'
12
+ require 'guard/bosh/job_properties_loader'
13
+ require 'guard/bosh/job_repository'
14
+ require 'guard/bosh/notifier'
15
+ require 'guard/bosh/package_resolver'
16
+ require 'guard/bosh/template_checker'
17
+ require 'guard/bosh/template_renderer'
18
+
19
+ def initialize(options = {})
20
+ super
21
+
22
+ unless options.key?(:deployment_manifest)
23
+ fail 'Please specify the deployment_manifest in your Guardfile'
24
+ end
25
+
26
+ @deployment_manifest = Pathname.new(options[:deployment_manifest])
27
+ @change_assessor = options[:change_assessor]
28
+ @job_repository = options[:job_repository]
29
+ @template_checker = options[:template_checker]
30
+ @notifier = options[:notifier]
31
+ end
32
+
33
+ def start
34
+ reload_deployment_manifest
35
+ @notifier = Notifier.new
36
+ end
37
+
38
+ def run_all
39
+ errors = render_all_job_templates
40
+ notify_errors(errors)
41
+ end
42
+
43
+ def run_on_modifications(paths)
44
+ change_scope, job_name = @change_assessor.determine_scope(paths)
45
+ errors = case change_scope
46
+ when :all
47
+ reload_deployment_manifest
48
+ render_all_job_templates
49
+ when :all_templates_for_job
50
+ render_templates_for_job(
51
+ job_name, @job_repository.template_paths(job_name))
52
+ when :single_template
53
+ render_templates_for_job(job_name, paths)
54
+ end
55
+ notify_errors(errors)
56
+ end
57
+
58
+ private
59
+
60
+ def reload_deployment_manifest
61
+ manifest = YAML.load_file(@deployment_manifest)
62
+ @change_assessor = ChangeAssessor.new(@deployment_manifest)
63
+ @job_repository = JobRepository.new(manifest)
64
+ @template_checker = TemplateChecker.build(
65
+ deployment_manifest: manifest,
66
+ release_dir: Pathname.new('.')
67
+ )
68
+ end
69
+
70
+ def notify_errors(errors)
71
+ @notifier.notify(errors)
72
+ throw :task_has_failed unless errors.empty?
73
+ end
74
+
75
+ def render_all_job_templates
76
+ @job_repository.job_templates.flat_map do |job_name|
77
+ render_templates_for_job(job_name,
78
+ @job_repository.template_paths(job_name))
79
+ end
80
+ end
81
+
82
+ def render_templates_for_job(job_name, templates)
83
+ manifest_jobs = @job_repository.find_by_template(job_name)
84
+ template_results = manifest_jobs.product(
85
+ templates).flat_map do |manifest_job, template|
86
+ @template_checker.check(
87
+ manifest_job_name: manifest_job,
88
+ job_name: job_name,
89
+ template: template)
90
+ end
91
+ template_results.select { |tr| tr[:status] == :failure }
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,145 @@
1
+ require 'ipaddr'
2
+
3
+ module Guard
4
+ class Bosh
5
+ class ApplySpecification
6
+ def initialize(deployment_manifest:, package_resolver:)
7
+ @manifest = deployment_manifest
8
+ @package_resolver = package_resolver
9
+ end
10
+
11
+ def generate(properties:, job_name:)
12
+ {
13
+ 'deployment' => @manifest['name'],
14
+ 'configuration_hash' => '24292bab7264f00ada207768efa7018e2a2226fc',
15
+ 'job' => job(job_name),
16
+ 'packages' => packages(job_name),
17
+ 'resource_pool' => resource_pool(job_name),
18
+ 'networks' => network(job_name),
19
+ 'index' => 0,
20
+ 'properties' => properties,
21
+ 'persistent_disk' => persistent_disk(job_name),
22
+ 'rendered_templates_archive' => rendered_templates_archive
23
+ }
24
+ end
25
+
26
+ private
27
+
28
+ def job(job_name)
29
+ manifest_job = manifest_job_with_name(job_name)
30
+ {
31
+ 'name' => job_name,
32
+ 'template' => manifest_job['template'] ||
33
+ manifest_job['templates'].first['name'],
34
+ 'version' => '1',
35
+ 'sha1' => 'c7f277de5b283e5ceffe55674dc56fad2257ecab',
36
+ 'blobstore_id' => '8a66ab45-4831-4ce3-aa8f-313fe33a9891',
37
+ 'templates' => templates(manifest_job)
38
+ }
39
+ end
40
+
41
+ def packages(job_name)
42
+ manifest_job = manifest_job_with_name(job_name)
43
+ all_packages = template_names(manifest_job).flat_map do |template|
44
+ @package_resolver.resolve(template)
45
+ end
46
+ all_packages.inject({}) do |result, package|
47
+ result.merge(
48
+
49
+ package => {
50
+ 'name' => package,
51
+ 'version' => '1.0',
52
+ 'sha1' => 'b945ce51b3635bb0ebfb2207323514381bcee824',
53
+ 'blobstore_id' => '608c41bc-d491-4773-9812-8f24276eace1'
54
+ }
55
+
56
+ )
57
+ end
58
+ end
59
+
60
+ def templates(manifest_job)
61
+ template_names(manifest_job).map { |t| template(t) }
62
+ end
63
+
64
+ def template_names(manifest_job)
65
+ if manifest_job.key?('template')
66
+ Array(manifest_job['template'])
67
+ else
68
+ manifest_job['templates'].map { |t| t['name'] }
69
+ end
70
+ end
71
+
72
+ def template(name)
73
+ {
74
+ 'name' => name,
75
+ 'version' => '1',
76
+ 'sha1' => '88d6ea417857efda58916f9cb9bd5dd3a0f76f00',
77
+ 'blobstore_id' => '2356dff1-18fd-4314-a9bd-199b9d6c5c45'
78
+ }
79
+ end
80
+
81
+ def network(job_name)
82
+ manifest_job = manifest_job_with_name(job_name)
83
+ job_network = manifest_job['networks'].first
84
+ network_definition = @manifest['networks'].find do |n|
85
+ n['name'] == job_network['name']
86
+ end
87
+ {
88
+ job_network['name'] => {
89
+ 'cloud_properties' => network_definition['subnets'].first['cloud_properties'],
90
+ 'dns_record_name' => dns_record_name(
91
+ job_name, job_network['name'], @manifest['name']),
92
+ 'ip' => ip_address(job_network, network_definition),
93
+ 'netmask' => netmask(network_definition['subnets'].first['range']),
94
+ 'default' => %w(dns gateway)
95
+ }
96
+ }
97
+ end
98
+
99
+ def ip_address(job_network, network_definition)
100
+ if job_network.key?('static_ips') &&
101
+ Array(job_network['static_ips']).any?
102
+ job_network['static_ips'].first
103
+ else
104
+ # We could be better here and calculate the dynamic address properly
105
+ network_definition['subnets'].first['range'].split('/').first
106
+ end
107
+ end
108
+
109
+ def dns_record_name(job, network, deployment)
110
+ "0.#{job}.#{network}.#{deployment}.bosh".gsub('_', '-')
111
+ end
112
+
113
+ def netmask(range)
114
+ cidr = range.split('/').last
115
+ IPAddr.new('255.255.255.255').mask(cidr).to_s
116
+ end
117
+
118
+ def resource_pool(job_name)
119
+ manifest_job = manifest_job_with_name(job_name)
120
+ job_pool = @manifest['resource_pools'].find do |pool|
121
+ pool['name'] == manifest_job['resource_pool']
122
+ end
123
+ job_pool.delete_if do |k, _|
124
+ ! %w(cloud_properties name stemcell).include?(k)
125
+ end
126
+ end
127
+
128
+ def persistent_disk(job_name)
129
+ manifest_job = manifest_job_with_name(job_name)
130
+ manifest_job['persistent_disk']
131
+ end
132
+
133
+ def rendered_templates_archive
134
+ {
135
+ 'sha1' => 'c299ead74faf9ee9b47b3548e5df427e3e9a2c70',
136
+ 'blobstore_id' => '72fb06ef-0f40-4280-85e8-b5930e672308'
137
+ }
138
+ end
139
+
140
+ def manifest_job_with_name(name)
141
+ @manifest['jobs'].find { |j| j['name'] == name }
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,53 @@
1
+ module Guard
2
+ class Bosh
3
+ class ChangeAssessor
4
+ def initialize(deployment_manifest)
5
+ @deployment_manifest = deployment_manifest
6
+ end
7
+
8
+ def determine_scope(raw_paths)
9
+ paths = raw_paths.map { |p| Pathname.new(p) }
10
+ return :all if paths.include?(@deployment_manifest)
11
+
12
+ jobs = paths.select { |p| spec_path?(p) }.map do |p|
13
+ p.dirname.basename.to_s
14
+ end
15
+
16
+ spec_scope = scope(jobs, :all_templates_for_job)
17
+ return spec_scope if spec_scope
18
+
19
+ jobs = paths.select { |p| template_path?(p) }.map do |t|
20
+ template_job(t)
21
+ end
22
+ template_scope = scope(jobs, :single_template)
23
+ return template_scope if template_scope
24
+
25
+ :none
26
+ end
27
+
28
+ private
29
+
30
+ def spec_path?(path)
31
+ path.basename.to_s == 'spec'
32
+ end
33
+
34
+ def template_path?(path)
35
+ path.descend { |p| break true if p.basename.to_s == 'templates' }
36
+ end
37
+
38
+ def template_job(template_path)
39
+ template_path.ascend do |p|
40
+ break p.dirname.basename.to_s if p.basename.to_s == 'templates'
41
+ end
42
+ end
43
+
44
+ def scope(jobs, value)
45
+ if jobs.size > 1
46
+ :all
47
+ elsif jobs.any?
48
+ [value, jobs.first]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,17 @@
1
+ require 'deep_merge'
2
+
3
+ module Guard
4
+ class Bosh
5
+ class EffectivePropertiesCalculator
6
+ def initialize(loaders:)
7
+ @loaders = loaders
8
+ end
9
+
10
+ def calculate_effective_properties(job)
11
+ @loaders.inject({}) do |result, loader|
12
+ result.deep_merge!(loader.load_properties(job))
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ module Guard
2
+ class Bosh
3
+ class GlobalPropertiesLoader
4
+ def initialize(deployment_manifest:)
5
+ @deployment_manifest = deployment_manifest
6
+ end
7
+
8
+ def load_properties(_)
9
+ global_properties = @deployment_manifest['properties']
10
+ if global_properties.nil?
11
+ {}
12
+ else
13
+ global_properties
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,63 @@
1
+ require 'yaml'
2
+
3
+ module Guard
4
+ class Bosh
5
+ class JobDefaultPropertiesLoader
6
+ def initialize(release_dir:)
7
+ @release_dir = release_dir
8
+ end
9
+
10
+ def load_properties(job)
11
+ properties = job_spec(job[:job_name]).fetch('properties')
12
+ expand(
13
+ intermediate_properties(properties).merge(
14
+ default_properties(properties)
15
+ )
16
+ )
17
+ end
18
+
19
+ private
20
+
21
+ def default_properties(properties)
22
+ defaults_only = properties.map do |property, config|
23
+ [property, config['default']]
24
+ end
25
+ Hash[defaults_only].reject { |_k, v| v.nil? }
26
+ end
27
+
28
+ def intermediate_properties(properties)
29
+ intermediates = properties.keys.map do |key|
30
+ key.split('.')[0..-2].join('.')
31
+ end.sort.uniq
32
+ Hash[intermediates.zip(intermediates.map { |_t| {} })]
33
+ end
34
+
35
+ def job_spec(job_name)
36
+ YAML.load_file(@release_dir + 'jobs' + job_name + 'spec')
37
+ end
38
+
39
+ def expand(properties)
40
+ properties.each_with_object({}) do |property, expanded|
41
+ key, value = property
42
+ current = expanded
43
+ each_parent(key) do |parent|
44
+ current[parent] = {} unless current.key?(parent)
45
+ current = current[parent]
46
+ end
47
+ current[leaf(key)] = value
48
+ end
49
+ end
50
+
51
+ def each_parent(key)
52
+ path = key.split('.')
53
+ path[0...-1].each do |parent|
54
+ yield parent
55
+ end
56
+ end
57
+
58
+ def leaf(key)
59
+ key.split('.').last
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ module Guard
2
+ class Bosh
3
+ class JobPropertiesLoader
4
+ def initialize(deployment_manifest:)
5
+ @manifest = deployment_manifest
6
+ end
7
+
8
+ def load_properties(job)
9
+ manifest_job = @manifest['jobs'].find do |j|
10
+ j['name'] == job[:manifest_job_name]
11
+ end
12
+ if manifest_job
13
+ manifest_job['properties']
14
+ else
15
+ {}
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,39 @@
1
+ require 'pathname'
2
+
3
+ module Guard
4
+ class Bosh
5
+ class JobRepository
6
+ def initialize(deployment_manifest)
7
+ @manifest = deployment_manifest
8
+ end
9
+
10
+ def job_templates
11
+ @manifest['jobs'].flat_map { |j| template_names(j) }.sort.uniq
12
+ end
13
+
14
+ def find_by_template(job)
15
+ jobs_using_template = @manifest['jobs'].select do |manifest_job|
16
+ template_names(manifest_job).include?(job)
17
+ end
18
+ jobs_using_template.map { |j| j['name'] }
19
+ end
20
+
21
+ def template_paths(job)
22
+ job_dir = Pathname.new('jobs') + job
23
+ YAML.load_file(job_dir + 'spec')['templates'].keys.map do |template|
24
+ job_dir + 'templates' + template
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def template_names(manifest_job)
31
+ if manifest_job.key?('templates')
32
+ manifest_job['templates'].map { |t| t['name'] }
33
+ else
34
+ Array(manifest_job['template'])
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end