github.com/asifdxtreme/cli@v6.1.3-0.20150123051144-9ead8700b4ae+incompatible/cf/manifest/manifest_test.go (about) 1 package manifest_test 2 3 import ( 4 "runtime" 5 "strings" 6 7 "github.com/cloudfoundry/cli/cf/manifest" 8 "github.com/cloudfoundry/cli/generic" 9 . "github.com/onsi/ginkgo" 10 . "github.com/onsi/gomega" 11 12 . "github.com/cloudfoundry/cli/testhelpers/matchers" 13 ) 14 15 func NewManifest(path string, data generic.Map) (m *manifest.Manifest) { 16 return &manifest.Manifest{Path: path, Data: data} 17 } 18 19 var _ = Describe("Manifests", func() { 20 It("merges global properties into each app's properties", func() { 21 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 22 "instances": "3", 23 "memory": "512M", 24 "applications": []interface{}{ 25 map[interface{}]interface{}{ 26 "name": "bitcoin-miner", 27 "no-route": true, 28 }, 29 }, 30 })) 31 32 apps, err := m.Applications() 33 Expect(err).NotTo(HaveOccurred()) 34 35 Expect(*apps[0].InstanceCount).To(Equal(3)) 36 Expect(*apps[0].Memory).To(Equal(int64(512))) 37 Expect(apps[0].NoRoute).To(BeTrue()) 38 }) 39 40 Describe("when there is no applications block", func() { 41 It("returns a single application with the global properties", func() { 42 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 43 "instances": "3", 44 "memory": "512M", 45 })) 46 47 apps, err := m.Applications() 48 Expect(err).NotTo(HaveOccurred()) 49 50 Expect(len(apps)).To(Equal(1)) 51 Expect(*apps[0].InstanceCount).To(Equal(3)) 52 Expect(*apps[0].Memory).To(Equal(int64(512))) 53 }) 54 }) 55 56 It("returns an error when the memory limit doesn't have a unit", func() { 57 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 58 "instances": "3", 59 "memory": "512", 60 "applications": []interface{}{ 61 map[interface{}]interface{}{ 62 "name": "bitcoin-miner", 63 }, 64 }, 65 })) 66 67 _, err := m.Applications() 68 Expect(err).To(HaveOccurred()) 69 Expect(err.Error()).To(ContainSubstring("Invalid value for 'memory': 512")) 70 }) 71 72 //candiedyaml returns an integer value when no unit is provided 73 It("returns an error when the memory limit is a non-string", func() { 74 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 75 "instances": "3", 76 "memory": 128, 77 "applications": []interface{}{ 78 map[interface{}]interface{}{ 79 "name": "bitcoin-miner", 80 }, 81 }, 82 })) 83 84 _, err := m.Applications() 85 Expect(err).To(HaveOccurred()) 86 Expect(err.Error()).To(ContainSubstring("Invalid value for 'memory': 128")) 87 }) 88 89 It("sets applications' health check timeouts", func() { 90 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 91 "applications": []interface{}{ 92 map[interface{}]interface{}{ 93 "name": "bitcoin-miner", 94 "timeout": "360", 95 }, 96 }, 97 })) 98 99 apps, err := m.Applications() 100 Expect(err).NotTo(HaveOccurred()) 101 Expect(*apps[0].HealthCheckTimeout).To(Equal(360)) 102 }) 103 104 It("allows boolean env var values", func() { 105 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 106 "env": generic.NewMap(map[interface{}]interface{}{ 107 "bar": true, 108 }), 109 })) 110 111 _, err := m.Applications() 112 Expect(err).ToNot(HaveOccurred()) 113 }) 114 115 It("does not allow nil values for environment variables", func() { 116 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 117 "env": generic.NewMap(map[interface{}]interface{}{ 118 "bar": nil, 119 }), 120 "applications": []interface{}{ 121 map[interface{}]interface{}{ 122 "name": "bad app", 123 }, 124 }, 125 })) 126 127 _, err := m.Applications() 128 Expect(err).To(HaveOccurred()) 129 Expect(err.Error()).To(ContainSubstring("env var 'bar' should not be null")) 130 }) 131 132 It("returns an empty map when no env was present in the manifest", func() { 133 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 134 "applications": []interface{}{ 135 map[interface{}]interface{}{"name": "no-env-vars"}, 136 }, 137 })) 138 139 apps, err := m.Applications() 140 Expect(err).NotTo(HaveOccurred()) 141 Expect(*apps[0].EnvironmentVars).NotTo(BeNil()) 142 }) 143 144 It("allows applications to have absolute paths", func() { 145 if runtime.GOOS == "windows" { 146 m := NewManifest(`C:\some\path\manifest.yml`, generic.NewMap(map[interface{}]interface{}{ 147 "applications": []interface{}{ 148 map[interface{}]interface{}{ 149 "path": `C:\another\path`, 150 }, 151 }, 152 })) 153 154 apps, err := m.Applications() 155 Expect(err).NotTo(HaveOccurred()) 156 Expect(*apps[0].Path).To(Equal(`C:\another\path`)) 157 } else { 158 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 159 "applications": []interface{}{ 160 map[interface{}]interface{}{ 161 "path": "/another/path-segment", 162 }, 163 }, 164 })) 165 166 apps, err := m.Applications() 167 Expect(err).NotTo(HaveOccurred()) 168 Expect(*apps[0].Path).To(Equal("/another/path-segment")) 169 } 170 }) 171 172 It("expands relative app paths based on the manifest's path", func() { 173 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 174 "applications": []interface{}{ 175 map[interface{}]interface{}{ 176 "path": "../another/path-segment", 177 }, 178 }, 179 })) 180 181 apps, err := m.Applications() 182 Expect(err).NotTo(HaveOccurred()) 183 if runtime.GOOS == "windows" { 184 Expect(*apps[0].Path).To(Equal("\\some\\another\\path-segment")) 185 } else { 186 Expect(*apps[0].Path).To(Equal("/some/another/path-segment")) 187 } 188 }) 189 190 It("returns errors when there are null values", func() { 191 m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{ 192 "applications": []interface{}{ 193 map[interface{}]interface{}{ 194 "disk_quota": nil, 195 "domain": nil, 196 "host": nil, 197 "name": nil, 198 "path": nil, 199 "stack": nil, 200 "memory": nil, 201 "instances": nil, 202 "timeout": nil, 203 "no-route": nil, 204 "services": nil, 205 "env": nil, 206 "random-route": nil, 207 }, 208 }, 209 })) 210 211 _, err := m.Applications() 212 Expect(err).To(HaveOccurred()) 213 errorSlice := strings.Split(err.Error(), "\n") 214 manifestKeys := []string{"disk_quota", "domain", "host", "name", "path", "stack", 215 "memory", "instances", "timeout", "no-route", "services", "env", "random-route"} 216 217 for _, key := range manifestKeys { 218 Expect(errorSlice).To(ContainSubstrings([]string{key, "not be null"})) 219 } 220 }) 221 222 It("parses known manifest keys", func() { 223 m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{ 224 "applications": []interface{}{ 225 map[interface{}]interface{}{ 226 "buildpack": "my-buildpack", 227 "disk_quota": "512M", 228 "domain": "my-domain", 229 "host": "my-hostname", 230 "hosts": []interface{}{"host-1", "host-2"}, 231 "name": "my-app-name", 232 "stack": "my-stack", 233 "memory": "256M", 234 "instances": 1, 235 "timeout": 11, 236 "no-route": true, 237 "random-route": true, 238 }, 239 }, 240 })) 241 242 apps, err := m.Applications() 243 Expect(err).NotTo(HaveOccurred()) 244 Expect(len(apps)).To(Equal(1)) 245 246 Expect(*apps[0].BuildpackUrl).To(Equal("my-buildpack")) 247 Expect(*apps[0].DiskQuota).To(Equal(int64(512))) 248 Expect(*apps[0].Domain).To(Equal("my-domain")) 249 Expect(*apps[0].Hosts).To(Equal([]string{"host-1", "host-2", "my-hostname"})) 250 Expect(*apps[0].Name).To(Equal("my-app-name")) 251 Expect(*apps[0].StackName).To(Equal("my-stack")) 252 Expect(*apps[0].Memory).To(Equal(int64(256))) 253 Expect(*apps[0].InstanceCount).To(Equal(1)) 254 Expect(*apps[0].HealthCheckTimeout).To(Equal(11)) 255 Expect(apps[0].NoRoute).To(BeTrue()) 256 Expect(apps[0].UseRandomHostname).To(BeTrue()) 257 }) 258 259 Describe("old-style property syntax", func() { 260 It("returns an error when the manifest contains non-whitelist properties", func() { 261 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 262 "applications": []interface{}{ 263 generic.NewMap(map[interface{}]interface{}{ 264 "env": generic.NewMap(map[interface{}]interface{}{ 265 "bar": "many-${some_property-name}-are-cool", 266 }), 267 }), 268 }, 269 })) 270 271 _, err := m.Applications() 272 Expect(err).To(HaveOccurred()) 273 Expect(err.Error()).To(ContainSubstring("'${some_property-name}'")) 274 }) 275 276 It("replaces the '${random-word} with a combination of 2 random words", func() { 277 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 278 "applications": []interface{}{ 279 generic.NewMap(map[interface{}]interface{}{ 280 "env": generic.NewMap(map[interface{}]interface{}{ 281 "bar": "prefix_${random-word}_suffix", 282 "foo": "some-value", 283 }), 284 }), 285 }, 286 })) 287 288 apps, err := m.Applications() 289 Expect(err).NotTo(HaveOccurred()) 290 Expect((*apps[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`)) 291 Expect((*apps[0].EnvironmentVars)["foo"]).To(Equal("some-value")) 292 293 apps2, _ := m.Applications() 294 Expect((*apps2[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`)) 295 Expect((*apps2[0].EnvironmentVars)["bar"]).NotTo(Equal((*apps[0].EnvironmentVars)["bar"])) 296 }) 297 }) 298 299 It("sets the command and buildpack to blank when their values are null in the manifest", func() { 300 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 301 "applications": []interface{}{ 302 generic.NewMap(map[interface{}]interface{}{ 303 "buildpack": nil, 304 "command": nil, 305 }), 306 }, 307 })) 308 309 apps, err := m.Applications() 310 Expect(err).NotTo(HaveOccurred()) 311 Expect(*apps[0].Command).To(Equal("")) 312 Expect(*apps[0].BuildpackUrl).To(Equal("")) 313 }) 314 315 It("sets the command and buildpack to blank when their values are 'default' in the manifest", func() { 316 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 317 "applications": []interface{}{ 318 generic.NewMap(map[interface{}]interface{}{ 319 "command": "default", 320 "buildpack": "default", 321 }), 322 }, 323 })) 324 325 apps, err := m.Applications() 326 Expect(err).NotTo(HaveOccurred()) 327 Expect(*apps[0].Command).To(Equal("")) 328 Expect(*apps[0].BuildpackUrl).To(Equal("")) 329 }) 330 331 It("does not set the start command when the manifest doesn't have the 'command' key", func() { 332 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 333 "applications": []interface{}{ 334 map[interface{}]interface{}{}, 335 }, 336 })) 337 338 apps, err := m.Applications() 339 Expect(err).NotTo(HaveOccurred()) 340 Expect(apps[0].Command).To(BeNil()) 341 }) 342 343 It("can build the applications multiple times", func() { 344 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 345 "memory": "254m", 346 "applications": []interface{}{ 347 map[interface{}]interface{}{ 348 "name": "bitcoin-miner", 349 }, 350 map[interface{}]interface{}{ 351 "name": "bitcoin-miner", 352 }, 353 }, 354 })) 355 356 apps1, err := m.Applications() 357 Expect(err).NotTo(HaveOccurred()) 358 359 apps2, err := m.Applications() 360 Expect(err).NotTo(HaveOccurred()) 361 Expect(apps1).To(Equal(apps2)) 362 }) 363 364 Describe("parsing env vars", func() { 365 It("handles values that are not strings", func() { 366 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 367 "applications": []interface{}{ 368 generic.NewMap(map[interface{}]interface{}{ 369 "env": map[interface{}]interface{}{ 370 "string-key": "value", 371 "int-key": 1, 372 "float-key": 11.1, 373 }, 374 }), 375 }, 376 })) 377 378 app, err := m.Applications() 379 Expect(err).NotTo(HaveOccurred()) 380 381 Expect((*app[0].EnvironmentVars)["string-key"]).To(Equal("value")) 382 Expect((*app[0].EnvironmentVars)["int-key"]).To(Equal(1)) 383 Expect((*app[0].EnvironmentVars)["float-key"]).To(Equal(11.1)) 384 }) 385 386 XIt("handles values that cannot be converted to strings", func() { 387 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 388 "applications": []interface{}{ 389 generic.NewMap(map[interface{}]interface{}{ 390 "env": map[interface{}]interface{}{ 391 "bad-key": map[interface{}]interface{}{}, 392 }, 393 }), 394 }, 395 })) 396 397 _, err := m.Applications() 398 Expect(err).To(HaveOccurred()) 399 }) 400 }) 401 402 Describe("parsing services", func() { 403 It("can read a list of service instance names", func() { 404 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 405 "services": []interface{}{"service-1", "service-2"}, 406 })) 407 408 app, err := m.Applications() 409 Expect(err).NotTo(HaveOccurred()) 410 411 Expect(*app[0].ServicesToBind).To(Equal([]string{"service-1", "service-2"})) 412 }) 413 }) 414 })