vagrant-bosh 0.0.1

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.
Files changed (147) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.gitmodules +6 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE +20 -0
  6. data/README.md +73 -0
  7. data/Rakefile +2 -0
  8. data/dev/.gitignore +1 -0
  9. data/dev/Vagrantfile +30 -0
  10. data/dev/example-bosh-manifest.yml +188 -0
  11. data/dev/example-winston-manifest.yml +70 -0
  12. data/go/.gitignore +11 -0
  13. data/go/bin/build +10 -0
  14. data/go/bin/build-linux-amd64 +9 -0
  15. data/go/bin/env +13 -0
  16. data/go/bin/go +5 -0
  17. data/go/bin/golint +19 -0
  18. data/go/bin/test +37 -0
  19. data/go/src/boshprovisioner/agent/client/client_interface.go +73 -0
  20. data/go/src/boshprovisioner/agent/client/fakes/fake_client.go +81 -0
  21. data/go/src/boshprovisioner/agent/client/http_client.go +299 -0
  22. data/go/src/boshprovisioner/agent/client/http_client_envelope.go +107 -0
  23. data/go/src/boshprovisioner/deployment/deployment.go +221 -0
  24. data/go/src/boshprovisioner/deployment/instance.go +54 -0
  25. data/go/src/boshprovisioner/deployment/manifest/deployment.go +80 -0
  26. data/go/src/boshprovisioner/deployment/manifest/ips.go +23 -0
  27. data/go/src/boshprovisioner/deployment/manifest/manifest.go +143 -0
  28. data/go/src/boshprovisioner/deployment/manifest/manifest_suite_test.go +13 -0
  29. data/go/src/boshprovisioner/deployment/manifest/manifest_test.go +86 -0
  30. data/go/src/boshprovisioner/deployment/manifest/syntax_validator.go +186 -0
  31. data/go/src/boshprovisioner/deployment/manifest/watch_time.go +47 -0
  32. data/go/src/boshprovisioner/deployment/manifest_reader.go +46 -0
  33. data/go/src/boshprovisioner/deployment/reader_factory.go +25 -0
  34. data/go/src/boshprovisioner/deployment/semantic_validator.go +111 -0
  35. data/go/src/boshprovisioner/downloader/blobstore_downloader.go +55 -0
  36. data/go/src/boshprovisioner/downloader/default_mux_downloader.go +22 -0
  37. data/go/src/boshprovisioner/downloader/downloader_interface.go +6 -0
  38. data/go/src/boshprovisioner/downloader/http_downloader.go +53 -0
  39. data/go/src/boshprovisioner/downloader/local_fs_downloader.go +48 -0
  40. data/go/src/boshprovisioner/downloader/mux_downloader.go +69 -0
  41. data/go/src/boshprovisioner/eventlog/log.go +72 -0
  42. data/go/src/boshprovisioner/eventlog/stage.go +39 -0
  43. data/go/src/boshprovisioner/eventlog/task.go +58 -0
  44. data/go/src/boshprovisioner/index/file_index.go +289 -0
  45. data/go/src/boshprovisioner/index/file_index_test.go +296 -0
  46. data/go/src/boshprovisioner/index/index_interface.go +18 -0
  47. data/go/src/boshprovisioner/index/index_suite_test.go +13 -0
  48. data/go/src/boshprovisioner/instance/templatescompiler/concrete_templates_compiler.go +273 -0
  49. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/erb_renderer.go +117 -0
  50. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/erbrenderer_suite_test.go +13 -0
  51. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/render_properties.go +77 -0
  52. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/render_properties_test.go +142 -0
  53. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/template_evaluation_context.go +85 -0
  54. data/go/src/boshprovisioner/instance/templatescompiler/erbrenderer/template_evaluation_context_rb.go +155 -0
  55. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/concrete_jobs_repository.go +64 -0
  56. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/concrete_runtime_packages_repository.go +105 -0
  57. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/concrete_template_to_job_repository.go +76 -0
  58. data/go/src/boshprovisioner/instance/templatescompiler/jobsrepo/jobs_repository_interface.go +31 -0
  59. data/go/src/boshprovisioner/instance/templatescompiler/rendered_archives_compiler.go +81 -0
  60. data/go/src/boshprovisioner/instance/templatescompiler/templates_compiler_interface.go +20 -0
  61. data/go/src/boshprovisioner/instance/templatescompiler/templatesrepo/ct_repository.go +54 -0
  62. data/go/src/boshprovisioner/instance/templatescompiler/templatesrepo/templates_repository_interface.go +16 -0
  63. data/go/src/boshprovisioner/instance/updater/applier/applier.go +93 -0
  64. data/go/src/boshprovisioner/instance/updater/applier/empty_state.go +66 -0
  65. data/go/src/boshprovisioner/instance/updater/applier/job_state.go +178 -0
  66. data/go/src/boshprovisioner/instance/updater/drainer.go +72 -0
  67. data/go/src/boshprovisioner/instance/updater/preparer.go +39 -0
  68. data/go/src/boshprovisioner/instance/updater/starter.go +36 -0
  69. data/go/src/boshprovisioner/instance/updater/stopper.go +36 -0
  70. data/go/src/boshprovisioner/instance/updater/updater.go +102 -0
  71. data/go/src/boshprovisioner/instance/updater/updater_factory.go +83 -0
  72. data/go/src/boshprovisioner/instance/updater/updater_suite_test.go +13 -0
  73. data/go/src/boshprovisioner/instance/updater/waiter.go +77 -0
  74. data/go/src/boshprovisioner/instance/updater/waiter_test.go +103 -0
  75. data/go/src/boshprovisioner/main/config.go +77 -0
  76. data/go/src/boshprovisioner/main/main.go +183 -0
  77. data/go/src/boshprovisioner/main/repos_factory.go +96 -0
  78. data/go/src/boshprovisioner/packagescompiler/compiledpackagesrepo/compiled_packages_repository_interface.go +17 -0
  79. data/go/src/boshprovisioner/packagescompiler/compiledpackagesrepo/concrete_compiled_packages_repository.go +61 -0
  80. data/go/src/boshprovisioner/packagescompiler/concrete_packages_compiler.go +179 -0
  81. data/go/src/boshprovisioner/packagescompiler/concrete_packages_compiler_factory.go +48 -0
  82. data/go/src/boshprovisioner/packagescompiler/packages_compiler_interface.go +20 -0
  83. data/go/src/boshprovisioner/packagescompiler/packagesrepo/concrete_packages_repository.go +65 -0
  84. data/go/src/boshprovisioner/packagescompiler/packagesrepo/packages_repository_interface.go +16 -0
  85. data/go/src/boshprovisioner/provisioner/blobstore_config.go +65 -0
  86. data/go/src/boshprovisioner/provisioner/blobstore_provisioner.go +38 -0
  87. data/go/src/boshprovisioner/provisioner/deployment_provisioner.go +97 -0
  88. data/go/src/boshprovisioner/provisioner/instance_provisioner.go +48 -0
  89. data/go/src/boshprovisioner/provisioner/release_compiler.go +133 -0
  90. data/go/src/boshprovisioner/release/job/job.go +86 -0
  91. data/go/src/boshprovisioner/release/job/manifest/manifest.go +79 -0
  92. data/go/src/boshprovisioner/release/job/manifest/manifest_suite_test.go +13 -0
  93. data/go/src/boshprovisioner/release/job/manifest/manifest_test.go +42 -0
  94. data/go/src/boshprovisioner/release/job/manifest/syntax_validator.go +43 -0
  95. data/go/src/boshprovisioner/release/job/reader_factory.go +34 -0
  96. data/go/src/boshprovisioner/release/job/tar_reader.go +133 -0
  97. data/go/src/boshprovisioner/release/manifest/manifest.go +96 -0
  98. data/go/src/boshprovisioner/release/manifest_reader.go +29 -0
  99. data/go/src/boshprovisioner/release/reader_factory.go +34 -0
  100. data/go/src/boshprovisioner/release/release.go +144 -0
  101. data/go/src/boshprovisioner/release/release_suite_test.go +13 -0
  102. data/go/src/boshprovisioner/release/release_test.go +129 -0
  103. data/go/src/boshprovisioner/release/tar_reader.go +139 -0
  104. data/go/src/boshprovisioner/releasesrepo/blobstore_releases_repository.go +114 -0
  105. data/go/src/boshprovisioner/releasesrepo/releases_repository_interface.go +15 -0
  106. data/go/src/boshprovisioner/tar/cmd_compressor.go +68 -0
  107. data/go/src/boshprovisioner/tar/cmd_extractor.go +47 -0
  108. data/go/src/boshprovisioner/tar/compressor_interface.go +6 -0
  109. data/go/src/boshprovisioner/tar/extractor_interface.go +6 -0
  110. data/go/src/boshprovisioner/util/string_keyed.go +70 -0
  111. data/go/src/boshprovisioner/vm/agent_provisioner.go +266 -0
  112. data/go/src/boshprovisioner/vm/asset_manager.go +61 -0
  113. data/go/src/boshprovisioner/vm/deps_provisioner.go +92 -0
  114. data/go/src/boshprovisioner/vm/monit_provisioner.go +83 -0
  115. data/go/src/boshprovisioner/vm/runit_provisioner.go +225 -0
  116. data/go/src/boshprovisioner/vm/simple_cmds.go +54 -0
  117. data/go/src/boshprovisioner/vm/vcap_user_provisioner.go +120 -0
  118. data/go/src/boshprovisioner/vm/vm.go +19 -0
  119. data/go/src/boshprovisioner/vm/vm_provisioner.go +57 -0
  120. data/go/src/boshprovisioner/vm/vm_provisioner_factory.go +97 -0
  121. data/lib/vagrant-bosh/asset_uploader.rb +53 -0
  122. data/lib/vagrant-bosh/assets/agent/agent-log +5 -0
  123. data/lib/vagrant-bosh/assets/agent/agent-run +12 -0
  124. data/lib/vagrant-bosh/assets/agent/agent.cert +18 -0
  125. data/lib/vagrant-bosh/assets/agent/agent.json +9 -0
  126. data/lib/vagrant-bosh/assets/agent/agent.key +27 -0
  127. data/lib/vagrant-bosh/assets/agent/bosh-agent +0 -0
  128. data/lib/vagrant-bosh/assets/agent/bosh-agent-rc +18 -0
  129. data/lib/vagrant-bosh/assets/agent/bosh-blobstore-dav +0 -0
  130. data/lib/vagrant-bosh/assets/monit/monit +0 -0
  131. data/lib/vagrant-bosh/assets/monit/monit-log +5 -0
  132. data/lib/vagrant-bosh/assets/monit/monit-run +9 -0
  133. data/lib/vagrant-bosh/assets/monit/monitrc +8 -0
  134. data/lib/vagrant-bosh/assets/provisioner +0 -0
  135. data/lib/vagrant-bosh/bootstrapper.rb +59 -0
  136. data/lib/vagrant-bosh/communicator.rb +50 -0
  137. data/lib/vagrant-bosh/config.rb +15 -0
  138. data/lib/vagrant-bosh/errors.rb +11 -0
  139. data/lib/vagrant-bosh/plugin.rb +25 -0
  140. data/lib/vagrant-bosh/provisioner.rb +46 -0
  141. data/lib/vagrant-bosh/provisioner_tracker.rb +41 -0
  142. data/lib/vagrant-bosh/ui.rb +77 -0
  143. data/lib/vagrant-bosh/version.rb +5 -0
  144. data/lib/vagrant-bosh.rb +15 -0
  145. data/templates/locales/en.yml +15 -0
  146. data/vagrant-bosh.gemspec +20 -0
  147. metadata +191 -0
@@ -0,0 +1,273 @@
1
+ package templatescompiler
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ boshblob "bosh/blobstore"
7
+ bosherr "bosh/errors"
8
+ boshlog "bosh/logger"
9
+
10
+ bpdep "boshprovisioner/deployment"
11
+ bpjobsrepo "boshprovisioner/instance/templatescompiler/jobsrepo"
12
+ bptplsrepo "boshprovisioner/instance/templatescompiler/templatesrepo"
13
+ bprel "boshprovisioner/release"
14
+ bpreljob "boshprovisioner/release/job"
15
+ )
16
+
17
+ type ConcreteTemplatesCompiler struct {
18
+ renderedArchivesCompiler RenderedArchivesCompiler
19
+ jobReaderFactory bpreljob.ReaderFactory
20
+
21
+ jobsRepo bpjobsrepo.JobsRepository
22
+ tplToJobRepo bpjobsrepo.TemplateToJobRepository
23
+ runPkgsRepo bpjobsrepo.RuntimePackagesRepository
24
+ templatesRepo bptplsrepo.TemplatesRepository
25
+
26
+ blobstore boshblob.Blobstore
27
+ logger boshlog.Logger
28
+ }
29
+
30
+ func NewConcreteTemplatesCompiler(
31
+ renderedArchivesCompiler RenderedArchivesCompiler,
32
+ jobReaderFactory bpreljob.ReaderFactory,
33
+ jobsRepo bpjobsrepo.JobsRepository,
34
+ tplToJobRepo bpjobsrepo.TemplateToJobRepository,
35
+ runPkgsRepo bpjobsrepo.RuntimePackagesRepository,
36
+ templatesRepo bptplsrepo.TemplatesRepository,
37
+ blobstore boshblob.Blobstore,
38
+ logger boshlog.Logger,
39
+ ) ConcreteTemplatesCompiler {
40
+ return ConcreteTemplatesCompiler{
41
+ renderedArchivesCompiler: renderedArchivesCompiler,
42
+ jobReaderFactory: jobReaderFactory,
43
+
44
+ jobsRepo: jobsRepo,
45
+ tplToJobRepo: tplToJobRepo,
46
+ runPkgsRepo: runPkgsRepo,
47
+ templatesRepo: templatesRepo,
48
+
49
+ blobstore: blobstore,
50
+ logger: logger,
51
+ }
52
+ }
53
+
54
+ // Precompile prepares release jobs to be later combined with instance properties
55
+ func (tc ConcreteTemplatesCompiler) Precompile(release bprel.Release) error {
56
+ var allPkgs []bprel.Package
57
+
58
+ for _, pkg := range release.Packages {
59
+ if pkg == nil {
60
+ // todo panic or should not be here?
61
+ return bosherr.New("Expected release to not have nil package")
62
+ }
63
+
64
+ allPkgs = append(allPkgs, *pkg)
65
+ }
66
+
67
+ for _, job := range release.Jobs {
68
+ jobRec, found, err := tc.jobsRepo.Find(job)
69
+ if err != nil {
70
+ return bosherr.WrapError(err, "Finding job source blob %s", job.Name)
71
+ }
72
+
73
+ if !found {
74
+ blobID, fingerprint, err := tc.blobstore.Create(job.TarPath)
75
+ if err != nil {
76
+ return bosherr.WrapError(err, "Creating job source blob %s", job.Name)
77
+ }
78
+
79
+ jobRec = bpjobsrepo.JobRecord{
80
+ BlobID: blobID,
81
+ SHA1: fingerprint,
82
+ }
83
+
84
+ err = tc.jobsRepo.Save(job, jobRec)
85
+ if err != nil {
86
+ return bosherr.WrapError(err, "Saving job record %s", job.Name)
87
+ }
88
+ }
89
+
90
+ err = tc.tplToJobRepo.SaveForJob(release, job)
91
+ if err != nil {
92
+ return bosherr.WrapError(err, "Saving release job %s", job.Name)
93
+ }
94
+
95
+ // todo associate to release instead
96
+ err = tc.runPkgsRepo.SaveAllForReleaseJob(job, allPkgs)
97
+ if err != nil {
98
+ return bosherr.WrapError(err, "Saving release job %s", job.Name)
99
+ }
100
+ }
101
+
102
+ return nil
103
+ }
104
+
105
+ // Compile populates blobstore with rendered jobs for a given deployment instance.
106
+ func (tc ConcreteTemplatesCompiler) Compile(job bpdep.Job, instance bpdep.Instance) error {
107
+ relJobReaders, err := tc.buildJobReaders(job)
108
+ if err != nil {
109
+ return err
110
+ }
111
+
112
+ blobID, fingerprint, err := tc.compileJob(relJobReaders, instance)
113
+ if err != nil {
114
+ return err
115
+ }
116
+
117
+ templateRec := bptplsrepo.TemplateRecord{
118
+ BlobID: blobID,
119
+ SHA1: fingerprint,
120
+ }
121
+
122
+ err = tc.templatesRepo.Save(job, instance, templateRec)
123
+ if err != nil {
124
+ return bosherr.WrapError(err, "Saving compiled templates record %s", job.Name)
125
+ }
126
+
127
+ return nil
128
+ }
129
+
130
+ // FindPackages returns list of packages required to run job template.
131
+ // List of packages is usually specified in release job metadata.
132
+ func (tc ConcreteTemplatesCompiler) FindPackages(template bpdep.Template) ([]bprel.Package, error) {
133
+ var pkgs []bprel.Package
134
+
135
+ job, found, err := tc.tplToJobRepo.FindByTemplate(template)
136
+ if err != nil {
137
+ return pkgs, bosherr.WrapError(err, "Finding job by template %s", template.Name)
138
+ } else if !found {
139
+ return pkgs, bosherr.New("Expected to find job by template %s", template.Name)
140
+ }
141
+
142
+ pkgs, found, err = tc.runPkgsRepo.FindByReleaseJob(job)
143
+ if err != nil {
144
+ return pkgs, bosherr.WrapError(err, "Finding packages by job %s", job.Name)
145
+ } else if !found {
146
+ return pkgs, bosherr.New("Expected to find packages by job %s", job.Name)
147
+ }
148
+
149
+ return pkgs, nil
150
+ }
151
+
152
+ // FindRenderedArchive returns previously compiled template for a given instance.
153
+ // If such compiled template is not found, error is returned.
154
+ func (tc ConcreteTemplatesCompiler) FindRenderedArchive(job bpdep.Job, instance bpdep.Instance) (RenderedArchiveRecord, error) {
155
+ var renderedArchiveRec RenderedArchiveRecord
156
+
157
+ rec, found, err := tc.templatesRepo.Find(job, instance)
158
+ if err != nil {
159
+ return renderedArchiveRec, bosherr.WrapError(err, "Finding compiled templates %s", job.Name)
160
+ } else if !found {
161
+ return renderedArchiveRec, bosherr.New("Expected to find compiled templates %s", job.Name)
162
+ }
163
+
164
+ renderedArchiveRec.SHA1 = rec.SHA1
165
+ renderedArchiveRec.BlobID = rec.BlobID
166
+
167
+ return renderedArchiveRec, nil
168
+ }
169
+
170
+ type jobReader struct {
171
+ relJob bprel.Job
172
+ tarReader *bpreljob.TarReader
173
+ }
174
+
175
+ func (tc ConcreteTemplatesCompiler) buildJobReaders(job bpdep.Job) ([]jobReader, error) {
176
+ var readers []jobReader
177
+
178
+ for _, template := range job.Templates {
179
+ relJob, found, err := tc.tplToJobRepo.FindByTemplate(template)
180
+ if err != nil {
181
+ return readers, bosherr.WrapError(err, "Finding dep-template -> rel-job %s", template.Name)
182
+ } else if !found {
183
+ return readers, bosherr.New("Expected to find dep-template -> rel-job %s", template.Name)
184
+ }
185
+
186
+ jobRec, found, err := tc.jobsRepo.Find(relJob)
187
+ if err != nil {
188
+ return readers, bosherr.WrapError(err, "Finding job source blob %s", template.Name)
189
+ } else if !found {
190
+ return readers, bosherr.New("Expected to find job source blob %s", template.Name)
191
+ }
192
+
193
+ jobURL := fmt.Sprintf("blobstore:///%s?fingerprint=%s", jobRec.BlobID, jobRec.SHA1)
194
+
195
+ reader := jobReader{
196
+ relJob: relJob,
197
+ tarReader: tc.jobReaderFactory.NewTarReader(jobURL),
198
+ }
199
+
200
+ readers = append(readers, reader)
201
+ }
202
+
203
+ return readers, nil
204
+ }
205
+
206
+ // compileJob produces and saves rendered templates archive to a blobstore.
207
+ func (tc ConcreteTemplatesCompiler) compileJob(jobReaders []jobReader, instance bpdep.Instance) (string, string, error) {
208
+ var relJobs []bpreljob.Job
209
+
210
+ for _, jobReader := range jobReaders {
211
+ relJob, err := jobReader.tarReader.Read()
212
+ if err != nil {
213
+ return "", "", bosherr.WrapError(err, "Reading job")
214
+ }
215
+
216
+ defer jobReader.tarReader.Close()
217
+
218
+ err = tc.associatePackages(jobReader.relJob, relJob)
219
+ if err != nil {
220
+ return "", "", bosherr.WrapError(err, "Preparing runtime dep packages")
221
+ }
222
+
223
+ relJobs = append(relJobs, relJob)
224
+ }
225
+
226
+ renderedArchivePath, err := tc.renderedArchivesCompiler.Compile(relJobs, instance)
227
+ if err != nil {
228
+ return "", "", bosherr.WrapError(err, "Compiling templates")
229
+ }
230
+
231
+ defer tc.renderedArchivesCompiler.CleanUp(renderedArchivePath)
232
+
233
+ blobID, fingerprint, err := tc.blobstore.Create(renderedArchivePath)
234
+ if err != nil {
235
+ return "", "", bosherr.WrapError(err, "Creating compiled templates")
236
+ }
237
+
238
+ return blobID, fingerprint, nil
239
+ }
240
+
241
+ func (tc ConcreteTemplatesCompiler) associatePackages(rJob bprel.Job, relJob bpreljob.Job) error {
242
+ _, found, err := tc.runPkgsRepo.FindByReleaseJob(rJob)
243
+ if err != nil {
244
+ return bosherr.WrapError(err, "Finding runtime deps for %s", rJob.Name)
245
+ }
246
+
247
+ if !found {
248
+ allPkgs, found, err := tc.runPkgsRepo.FindAllByReleaseJob(rJob)
249
+ if err != nil {
250
+ return bosherr.WrapError(err, "Finding rel-job -> rel-pkgs %s", rJob.Name)
251
+ } else if !found {
252
+ return bosherr.New("Expected to find rel-job -> rel-pkgs %s", rJob.Name)
253
+ }
254
+
255
+ var pkgs []bprel.Package
256
+
257
+ for _, pkg := range allPkgs {
258
+ for _, p := range relJob.Packages {
259
+ if pkg.Name == p.Name {
260
+ pkgs = append(pkgs, pkg)
261
+ break
262
+ }
263
+ }
264
+ }
265
+
266
+ err = tc.runPkgsRepo.SaveForReleaseJob(rJob, pkgs)
267
+ if err != nil {
268
+ return bosherr.WrapError(err, "Saving job packages %s", rJob.Name)
269
+ }
270
+ }
271
+
272
+ return nil
273
+ }
@@ -0,0 +1,117 @@
1
+ package erbrenderer
2
+
3
+ import (
4
+ "encoding/json"
5
+ "os"
6
+ "path/filepath"
7
+
8
+ bosherr "bosh/errors"
9
+ boshlog "bosh/logger"
10
+ boshsys "bosh/system"
11
+ )
12
+
13
+ const erbRendererLogTag = "ERBRenderer"
14
+
15
+ type ERBRenderer struct {
16
+ fs boshsys.FileSystem
17
+ runner boshsys.CmdRunner
18
+ context TemplateEvaluationContext
19
+ logger boshlog.Logger
20
+
21
+ rendererScript string
22
+ }
23
+
24
+ func NewERBRenderer(
25
+ fs boshsys.FileSystem,
26
+ runner boshsys.CmdRunner,
27
+ context TemplateEvaluationContext,
28
+ logger boshlog.Logger,
29
+ ) ERBRenderer {
30
+ return ERBRenderer{
31
+ fs: fs,
32
+ runner: runner,
33
+ context: context,
34
+ logger: logger,
35
+
36
+ rendererScript: templateEvaluationContextRb,
37
+ }
38
+ }
39
+
40
+ func (r ERBRenderer) Render(srcPath, dstPath string) error {
41
+ r.logger.Debug(erbRendererLogTag, "Rendering template %s", dstPath)
42
+
43
+ dirPath := filepath.Dir(dstPath)
44
+
45
+ err := r.fs.MkdirAll(dirPath, os.FileMode(0755))
46
+ if err != nil {
47
+ return bosherr.WrapError(err, "Creating directory %s", dirPath)
48
+ }
49
+
50
+ rendererScriptPath, err := r.writeRendererScript()
51
+ if err != nil {
52
+ return err
53
+ }
54
+
55
+ contextPath, err := r.writeContext()
56
+ if err != nil {
57
+ return err
58
+ }
59
+
60
+ // Use ruby to compile job templates
61
+ command := boshsys.Command{
62
+ Name: r.determineRubyExePath(),
63
+ Args: []string{rendererScriptPath, contextPath, srcPath, dstPath},
64
+ }
65
+
66
+ _, _, _, err = r.runner.RunComplexCommand(command)
67
+ if err != nil {
68
+ return bosherr.WrapError(err, "Running ruby")
69
+ }
70
+
71
+ return nil
72
+ }
73
+
74
+ func (r ERBRenderer) writeRendererScript() (string, error) {
75
+ // todo use temp path; it's same everytime?
76
+ path := "/tmp/erb-render.rb"
77
+
78
+ err := r.fs.WriteFileString(path, r.rendererScript)
79
+ if err != nil {
80
+ return "", bosherr.WrapError(err, "Writing renderer script")
81
+ }
82
+
83
+ return path, nil
84
+ }
85
+
86
+ func (r ERBRenderer) writeContext() (string, error) {
87
+ contextBytes, err := json.Marshal(r.context)
88
+ if err != nil {
89
+ return "", bosherr.WrapError(err, "Marshalling context")
90
+ }
91
+
92
+ // todo use temp path?
93
+ path := "/tmp/erb-context.json"
94
+
95
+ err = r.fs.WriteFileString(path, string(contextBytes))
96
+ if err != nil {
97
+ return "", bosherr.WrapError(err, "Writing context")
98
+ }
99
+
100
+ return path, nil
101
+ }
102
+
103
+ func (r ERBRenderer) determineRubyExePath() string {
104
+ // Prefer ruby executable on the PATH
105
+ if r.runner.CommandExists("ruby") {
106
+ return "ruby"
107
+ }
108
+
109
+ // Fallback to chef-solo ruby usually found in vagrant boxes
110
+ vagrantRuby := "/opt/vagrant_ruby/bin/ruby"
111
+
112
+ if r.fs.FileExists(vagrantRuby) {
113
+ return vagrantRuby
114
+ }
115
+
116
+ return "ruby"
117
+ }
@@ -0,0 +1,13 @@
1
+ package erbrenderer_test
2
+
3
+ import (
4
+ . "github.com/onsi/ginkgo"
5
+ . "github.com/onsi/gomega"
6
+
7
+ "testing"
8
+ )
9
+
10
+ func TestErbrenderer(t *testing.T) {
11
+ RegisterFailHandler(Fail)
12
+ RunSpecs(t, "Erbrenderer Suite")
13
+ }
@@ -0,0 +1,77 @@
1
+ package erbrenderer
2
+
3
+ import (
4
+ "encoding/json"
5
+ "strings"
6
+
7
+ bosherr "bosh/errors"
8
+
9
+ bpdep "boshprovisioner/deployment"
10
+ bpreljob "boshprovisioner/release/job"
11
+ )
12
+
13
+ type RenderProperties struct {
14
+ relJob bpreljob.Job
15
+ instance bpdep.Instance
16
+ }
17
+
18
+ func NewRenderProperties(relJob bpreljob.Job, instance bpdep.Instance) RenderProperties {
19
+ return RenderProperties{relJob: relJob, instance: instance}
20
+ }
21
+
22
+ // AsMap returns job and instance properties merged together.
23
+ func (p RenderProperties) AsMap() (map[string]interface{}, error) {
24
+ result, err := p.deepCopyInstanceProperties()
25
+ if err != nil {
26
+ return result, err
27
+ }
28
+
29
+ for _, prop := range p.relJob.Properties {
30
+ p.copyProperty(prop.Name, prop.Default, result)
31
+ }
32
+
33
+ return result, nil
34
+ }
35
+
36
+ // copyProperty fills in property in dst with value if it's not already set.
37
+ func (p RenderProperties) copyProperty(path string, value interface{}, dst map[string]interface{}) {
38
+ pathParts := strings.Split(path, ".")
39
+
40
+ for i, part := range pathParts {
41
+ if dstNested, ok := dst[part]; ok { // Found section; check if modified
42
+ if dstNestedMap, ok := dstNested.(map[string]interface{}); ok {
43
+ dst = dstNestedMap
44
+ } else {
45
+ break // Not a section; cannot modify
46
+ }
47
+ } else if len(pathParts)-1 == i { // Last property path part
48
+ dst[part] = value
49
+ } else {
50
+ m := map[string]interface{}{}
51
+ dst[part] = m
52
+ dst = m
53
+ }
54
+ }
55
+ }
56
+
57
+ // deepCopyInstanceProperties makes a deep copy of instance properties.
58
+ // Always returns an initialized map even if instance properties are nil.
59
+ func (p RenderProperties) deepCopyInstanceProperties() (map[string]interface{}, error) {
60
+ result := map[string]interface{}{}
61
+
62
+ if p.instance.Properties == nil {
63
+ return result, nil
64
+ }
65
+
66
+ bytes, err := json.Marshal(p.instance.Properties)
67
+ if err != nil {
68
+ return result, bosherr.WrapError(err, "Marshalling instance properties")
69
+ }
70
+
71
+ err = json.Unmarshal(bytes, &result)
72
+ if err != nil {
73
+ return result, bosherr.WrapError(err, "Unmarshalling instance properties")
74
+ }
75
+
76
+ return result, nil
77
+ }
@@ -0,0 +1,142 @@
1
+ package erbrenderer_test
2
+
3
+ import (
4
+ . "github.com/onsi/ginkgo"
5
+ . "github.com/onsi/gomega"
6
+
7
+ bpdep "boshprovisioner/deployment"
8
+ . "boshprovisioner/instance/templatescompiler/erbrenderer"
9
+ bpreljob "boshprovisioner/release/job"
10
+ )
11
+
12
+ var _ = Describe("RenderProperties", func() {
13
+ var (
14
+ job bpreljob.Job
15
+ instance bpdep.Instance
16
+ props RenderProperties
17
+ )
18
+
19
+ JustBeforeEach(func() {
20
+ props = NewRenderProperties(job, instance)
21
+ })
22
+
23
+ Describe("AsMap", func() {
24
+ Context("when job specifies a default for a nested property", func() {
25
+ BeforeEach(func() {
26
+ job = bpreljob.Job{
27
+ Properties: []bpreljob.Property{
28
+ bpreljob.Property{
29
+ Name: "prop.nest-prop",
30
+ Default: "job-val",
31
+ },
32
+ },
33
+ }
34
+ })
35
+
36
+ Context("when instance specifies nested property", func() {
37
+ BeforeEach(func() {
38
+ instance = bpdep.Instance{
39
+ Properties: map[string]interface{}{
40
+ "prop": map[string]interface{}{
41
+ "nest-prop": "instance-val",
42
+ },
43
+ },
44
+ }
45
+ })
46
+
47
+ It("returns map with property value from job", func() {
48
+ Expect(props.AsMap()).To(Equal(map[string]interface{}{
49
+ "prop": map[string]interface{}{
50
+ "nest-prop": "instance-val",
51
+ },
52
+ }))
53
+ })
54
+ })
55
+
56
+ Context("when instance does not specify nested property", func() {
57
+ Context("when first nesting level is specified", func() {
58
+ BeforeEach(func() {
59
+ instance = bpdep.Instance{
60
+ Properties: map[string]interface{}{
61
+ "prop": map[string]interface{}{},
62
+ },
63
+ }
64
+ })
65
+
66
+ It("returns map with default property from job", func() {
67
+ Expect(props.AsMap()).To(Equal(map[string]interface{}{
68
+ "prop": map[string]interface{}{
69
+ "nest-prop": "job-val",
70
+ },
71
+ }))
72
+ })
73
+ })
74
+
75
+ Context("when first nesting level is not specified", func() {
76
+ BeforeEach(func() {
77
+ instance = bpdep.Instance{}
78
+ })
79
+
80
+ It("returns map with default property from job", func() {
81
+ Expect(props.AsMap()).To(Equal(map[string]interface{}{
82
+ "prop": map[string]interface{}{
83
+ "nest-prop": "job-val",
84
+ },
85
+ }))
86
+ })
87
+ })
88
+ })
89
+ })
90
+
91
+ Context("when job does not specify a default (nil) for a nested property", func() {
92
+ BeforeEach(func() {
93
+ job = bpreljob.Job{
94
+ Properties: []bpreljob.Property{
95
+ bpreljob.Property{
96
+ Name: "prop.nest-prop",
97
+ Default: nil,
98
+ },
99
+ },
100
+ }
101
+ })
102
+
103
+ Context("when instance specifies nested property", func() {
104
+ BeforeEach(func() {
105
+ instance = bpdep.Instance{
106
+ Properties: map[string]interface{}{
107
+ "prop": map[string]interface{}{
108
+ "nest-prop": "instance-val",
109
+ },
110
+ },
111
+ }
112
+ })
113
+
114
+ It("returns map with property value from instance", func() {
115
+ Expect(props.AsMap()).To(Equal(map[string]interface{}{
116
+ "prop": map[string]interface{}{
117
+ "nest-prop": "instance-val",
118
+ },
119
+ }))
120
+ })
121
+ })
122
+
123
+ Context("when instance does not specify nested property", func() {
124
+ BeforeEach(func() {
125
+ instance = bpdep.Instance{
126
+ Properties: map[string]interface{}{
127
+ "prop": map[string]interface{}{},
128
+ },
129
+ }
130
+ })
131
+
132
+ It("returns map with default property from job", func() {
133
+ Expect(props.AsMap()).To(Equal(map[string]interface{}{
134
+ "prop": map[string]interface{}{
135
+ "nest-prop": nil,
136
+ },
137
+ }))
138
+ })
139
+ })
140
+ }) //~
141
+ })
142
+ })
@@ -0,0 +1,85 @@
1
+ package erbrenderer
2
+
3
+ import (
4
+ "encoding/json"
5
+
6
+ bosherr "bosh/errors"
7
+
8
+ bpdep "boshprovisioner/deployment"
9
+ bpreljob "boshprovisioner/release/job"
10
+ )
11
+
12
+ type TemplateEvaluationContext struct {
13
+ relJob bpreljob.Job
14
+ instance bpdep.Instance
15
+ }
16
+
17
+ // rootContext is exposed as an open struct in ERB templates.
18
+ // It must stay same to provide backwards compatible API.
19
+ type rootContext struct {
20
+ Index int `json:"index"`
21
+
22
+ JobContext jobContext `json:"job"`
23
+
24
+ Deployment string `json:"deployment"`
25
+
26
+ // Usually is accessed with <%= spec.networks.default.ip %>
27
+ NetworkContexts map[string]networkContext `json:"networks"`
28
+
29
+ Properties map[string]interface{} `json:"properties"`
30
+ }
31
+
32
+ type jobContext struct {
33
+ Name string `json:"name"`
34
+ }
35
+
36
+ // networkContext is not fully backwards compatible.
37
+ type networkContext struct {
38
+ IP string `json:"ip"`
39
+ Netmask string `json:"netmask"`
40
+ Gateway string `json:"gateway"`
41
+ }
42
+
43
+ func NewTemplateEvaluationContext(
44
+ relJob bpreljob.Job,
45
+ instance bpdep.Instance,
46
+ ) TemplateEvaluationContext {
47
+ return TemplateEvaluationContext{
48
+ relJob: relJob,
49
+ instance: instance,
50
+ }
51
+ }
52
+
53
+ func (c TemplateEvaluationContext) MarshalJSON() ([]byte, error) {
54
+ properties, err := NewRenderProperties(c.relJob, c.instance).AsMap()
55
+ if err != nil {
56
+ return nil, bosherr.WrapError(err, "Rendering properties")
57
+ }
58
+
59
+ context := rootContext{
60
+ Index: c.instance.Index,
61
+ JobContext: jobContext{Name: c.instance.JobName},
62
+ Deployment: c.instance.DeploymentName,
63
+
64
+ NetworkContexts: c.buildNetworkContexts(),
65
+ Properties: properties,
66
+ }
67
+
68
+ return json.Marshal(context)
69
+ }
70
+
71
+ func (c TemplateEvaluationContext) buildNetworkContexts() map[string]networkContext {
72
+ networkContexts := map[string]networkContext{}
73
+
74
+ for _, na := range c.instance.NetworkAssociations {
75
+ netConfig := c.instance.NetworkConfigurationForNetworkAssociation(na)
76
+
77
+ networkContexts[na.Network.Name] = networkContext{
78
+ IP: netConfig.IP,
79
+ Netmask: netConfig.Netmask,
80
+ Gateway: netConfig.Gateway,
81
+ }
82
+ }
83
+
84
+ return networkContexts
85
+ }