vagrant-bosh 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
+ }