github.com/rakutentech/cli@v6.12.5-0.20151006231303-24468b65536e+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 "no-hostname": nil, 205 "services": nil, 206 "env": nil, 207 "random-route": nil, 208 }, 209 }, 210 })) 211 212 _, err := m.Applications() 213 Expect(err).To(HaveOccurred()) 214 errorSlice := strings.Split(err.Error(), "\n") 215 manifestKeys := []string{"disk_quota", "domain", "host", "name", "path", "stack", 216 "memory", "instances", "timeout", "no-route", "no-hostname", "services", "env", "random-route"} 217 218 for _, key := range manifestKeys { 219 Expect(errorSlice).To(ContainSubstrings([]string{key, "not be null"})) 220 } 221 }) 222 223 It("returns errors when hosts/domains is not valid slice", func() { 224 m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{ 225 "applications": []interface{}{ 226 map[interface{}]interface{}{ 227 "hosts": "bad-value", 228 "domains": []interface{}{"val1", "val2", false, true}, 229 }, 230 }, 231 })) 232 233 _, err := m.Applications() 234 Expect(err).To(HaveOccurred()) 235 errorSlice := strings.Split(err.Error(), "\n") 236 237 Expect(errorSlice).To(ContainSubstrings([]string{"hosts", "to be a list of strings"})) 238 Expect(errorSlice).To(ContainSubstrings([]string{"domains", "to be a list of strings"})) 239 }) 240 241 It("parses known manifest keys", func() { 242 m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{ 243 "applications": []interface{}{ 244 map[interface{}]interface{}{ 245 "buildpack": "my-buildpack", 246 "disk_quota": "512M", 247 "domain": "my-domain", 248 "domains": []interface{}{"domain1.test", "domain2.test"}, 249 "host": "my-hostname", 250 "hosts": []interface{}{"host-1", "host-2"}, 251 "name": "my-app-name", 252 "stack": "my-stack", 253 "memory": "256M", 254 "instances": 1, 255 "timeout": 11, 256 "no-route": true, 257 "no-hostname": true, 258 "random-route": true, 259 }, 260 }, 261 })) 262 263 apps, err := m.Applications() 264 Expect(err).NotTo(HaveOccurred()) 265 Expect(len(apps)).To(Equal(1)) 266 267 Expect(*apps[0].BuildpackUrl).To(Equal("my-buildpack")) 268 Expect(*apps[0].DiskQuota).To(Equal(int64(512))) 269 Expect(*apps[0].Domains).To(ConsistOf([]string{"domain1.test", "domain2.test", "my-domain"})) 270 Expect(*apps[0].Hosts).To(ConsistOf([]string{"host-1", "host-2", "my-hostname"})) 271 Expect(*apps[0].Name).To(Equal("my-app-name")) 272 Expect(*apps[0].StackName).To(Equal("my-stack")) 273 Expect(*apps[0].Memory).To(Equal(int64(256))) 274 Expect(*apps[0].InstanceCount).To(Equal(1)) 275 Expect(*apps[0].HealthCheckTimeout).To(Equal(11)) 276 Expect(apps[0].NoRoute).To(BeTrue()) 277 Expect(apps[0].NoHostname).To(BeTrue()) 278 Expect(apps[0].UseRandomHostname).To(BeTrue()) 279 }) 280 281 It("removes duplicated values in 'hosts' and 'domains'", func() { 282 m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{ 283 "applications": []interface{}{ 284 map[interface{}]interface{}{ 285 "domain": "my-domain", 286 "domains": []interface{}{"my-domain", "domain1.test", "domain1.test", "domain2.test"}, 287 "host": "my-hostname", 288 "hosts": []interface{}{"my-hostname", "host-1", "host-1", "host-2"}, 289 "name": "my-app-name", 290 }, 291 }, 292 })) 293 294 apps, err := m.Applications() 295 Expect(err).NotTo(HaveOccurred()) 296 Expect(len(apps)).To(Equal(1)) 297 298 Expect(len(*apps[0].Domains)).To(Equal(3)) 299 Expect(*apps[0].Domains).To(ConsistOf([]string{"my-domain", "domain1.test", "domain2.test"})) 300 Expect(len(*apps[0].Hosts)).To(Equal(3)) 301 Expect(*apps[0].Hosts).To(ConsistOf([]string{"my-hostname", "host-1", "host-2"})) 302 }) 303 304 Describe("old-style property syntax", func() { 305 It("returns an error when the manifest contains non-whitelist properties", func() { 306 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 307 "applications": []interface{}{ 308 generic.NewMap(map[interface{}]interface{}{ 309 "env": generic.NewMap(map[interface{}]interface{}{ 310 "bar": "many-${some_property-name}-are-cool", 311 }), 312 }), 313 }, 314 })) 315 316 _, err := m.Applications() 317 Expect(err).To(HaveOccurred()) 318 Expect(err.Error()).To(ContainSubstring("'${some_property-name}'")) 319 }) 320 321 It("replaces the '${random-word} with a combination of 2 random words", func() { 322 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 323 "applications": []interface{}{ 324 generic.NewMap(map[interface{}]interface{}{ 325 "env": generic.NewMap(map[interface{}]interface{}{ 326 "bar": "prefix_${random-word}_suffix", 327 "foo": "some-value", 328 }), 329 }), 330 }, 331 })) 332 333 apps, err := m.Applications() 334 Expect(err).NotTo(HaveOccurred()) 335 Expect((*apps[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`)) 336 Expect((*apps[0].EnvironmentVars)["foo"]).To(Equal("some-value")) 337 338 apps2, _ := m.Applications() 339 Expect((*apps2[0].EnvironmentVars)["bar"]).To(MatchRegexp(`prefix_\w+-\w+_suffix`)) 340 Expect((*apps2[0].EnvironmentVars)["bar"]).NotTo(Equal((*apps[0].EnvironmentVars)["bar"])) 341 }) 342 }) 343 344 It("sets the command and buildpack to blank when their values are null in the manifest", func() { 345 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 346 "applications": []interface{}{ 347 generic.NewMap(map[interface{}]interface{}{ 348 "buildpack": nil, 349 "command": nil, 350 }), 351 }, 352 })) 353 354 apps, err := m.Applications() 355 Expect(err).NotTo(HaveOccurred()) 356 Expect(*apps[0].Command).To(Equal("")) 357 Expect(*apps[0].BuildpackUrl).To(Equal("")) 358 }) 359 360 It("sets the command and buildpack to blank when their values are 'default' in the manifest", func() { 361 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 362 "applications": []interface{}{ 363 generic.NewMap(map[interface{}]interface{}{ 364 "command": "default", 365 "buildpack": "default", 366 }), 367 }, 368 })) 369 370 apps, err := m.Applications() 371 Expect(err).NotTo(HaveOccurred()) 372 Expect(*apps[0].Command).To(Equal("")) 373 Expect(*apps[0].BuildpackUrl).To(Equal("")) 374 }) 375 376 It("does not set the start command when the manifest doesn't have the 'command' key", func() { 377 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 378 "applications": []interface{}{ 379 map[interface{}]interface{}{}, 380 }, 381 })) 382 383 apps, err := m.Applications() 384 Expect(err).NotTo(HaveOccurred()) 385 Expect(apps[0].Command).To(BeNil()) 386 }) 387 388 It("can build the applications multiple times", func() { 389 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 390 "memory": "254m", 391 "applications": []interface{}{ 392 map[interface{}]interface{}{ 393 "name": "bitcoin-miner", 394 }, 395 map[interface{}]interface{}{ 396 "name": "bitcoin-miner", 397 }, 398 }, 399 })) 400 401 apps1, err := m.Applications() 402 Expect(err).NotTo(HaveOccurred()) 403 404 apps2, err := m.Applications() 405 Expect(err).NotTo(HaveOccurred()) 406 Expect(apps1).To(Equal(apps2)) 407 }) 408 409 Describe("parsing env vars", func() { 410 It("handles values that are not strings", func() { 411 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 412 "applications": []interface{}{ 413 generic.NewMap(map[interface{}]interface{}{ 414 "env": map[interface{}]interface{}{ 415 "string-key": "value", 416 "int-key": 1, 417 "float-key": 11.1, 418 }, 419 }), 420 }, 421 })) 422 423 app, err := m.Applications() 424 Expect(err).NotTo(HaveOccurred()) 425 426 Expect((*app[0].EnvironmentVars)["string-key"]).To(Equal("value")) 427 Expect((*app[0].EnvironmentVars)["int-key"]).To(Equal(1)) 428 Expect((*app[0].EnvironmentVars)["float-key"]).To(Equal(11.1)) 429 }) 430 431 XIt("handles values that cannot be converted to strings", func() { 432 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 433 "applications": []interface{}{ 434 generic.NewMap(map[interface{}]interface{}{ 435 "env": map[interface{}]interface{}{ 436 "bad-key": map[interface{}]interface{}{}, 437 }, 438 }), 439 }, 440 })) 441 442 _, err := m.Applications() 443 Expect(err).To(HaveOccurred()) 444 }) 445 }) 446 447 Describe("parsing services", func() { 448 It("can read a list of service instance names", func() { 449 m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{ 450 "services": []interface{}{"service-1", "service-2"}, 451 })) 452 453 app, err := m.Applications() 454 Expect(err).NotTo(HaveOccurred()) 455 456 Expect(*app[0].ServicesToBind).To(Equal([]string{"service-1", "service-2"})) 457 }) 458 }) 459 })