github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/cf/manifest/manifest_test.go (about)

     1  package manifest_test
     2  
     3  import (
     4  	"runtime"
     5  	"strings"
     6  
     7  	"code.cloudfoundry.org/cli/cf/manifest"
     8  	"code.cloudfoundry.org/cli/util/generic"
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  
    12  	. "code.cloudfoundry.org/cli/cf/util/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("errors when provided multiple buildpacks", func() {
    21  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    22  			"applications": []interface{}{
    23  				map[interface{}]interface{}{
    24  					"name":       "bitcoin-miner",
    25  					"buildpacks": []interface{}{"a", "b"},
    26  				},
    27  			},
    28  		}))
    29  
    30  		_, err := m.Applications()
    31  		Expect(err).To(HaveOccurred())
    32  		Expect(err.Error()).To(MatchRegexp("The following manifest fields cannot be used with legacy push: buildpacks"))
    33  	})
    34  
    35  	It("merges global properties into each app's properties", func() {
    36  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    37  			"instances": "3",
    38  			"memory":    "512M",
    39  			"applications": []interface{}{
    40  				map[interface{}]interface{}{
    41  					"name":     "bitcoin-miner",
    42  					"no-route": true,
    43  				},
    44  			},
    45  		}))
    46  
    47  		apps, err := m.Applications()
    48  		Expect(err).NotTo(HaveOccurred())
    49  
    50  		Expect(*apps[0].InstanceCount).To(Equal(3))
    51  		Expect(*apps[0].Memory).To(Equal(int64(512)))
    52  		Expect(apps[0].NoRoute).To(BeTrue())
    53  	})
    54  
    55  	Context("when there is no applications block", func() {
    56  		It("returns a single application with the global properties", func() {
    57  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    58  				"instances": "3",
    59  				"memory":    "512M",
    60  			}))
    61  
    62  			apps, err := m.Applications()
    63  			Expect(err).NotTo(HaveOccurred())
    64  
    65  			Expect(len(apps)).To(Equal(1))
    66  			Expect(*apps[0].InstanceCount).To(Equal(3))
    67  			Expect(*apps[0].Memory).To(Equal(int64(512)))
    68  		})
    69  	})
    70  
    71  	It("returns an error when the memory limit doesn't have a unit", func() {
    72  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    73  			"instances": "3",
    74  			"memory":    "512",
    75  			"applications": []interface{}{
    76  				map[interface{}]interface{}{
    77  					"name": "bitcoin-miner",
    78  				},
    79  			},
    80  		}))
    81  
    82  		_, err := m.Applications()
    83  		Expect(err).To(HaveOccurred())
    84  		Expect(err.Error()).To(ContainSubstring("Invalid value for 'memory': 512"))
    85  	})
    86  
    87  	It("returns an error when the memory limit is a non-string", func() {
    88  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
    89  			"instances": "3",
    90  			"memory":    128,
    91  			"applications": []interface{}{
    92  				map[interface{}]interface{}{
    93  					"name": "bitcoin-miner",
    94  				},
    95  			},
    96  		}))
    97  
    98  		_, err := m.Applications()
    99  		Expect(err).To(HaveOccurred())
   100  		Expect(err.Error()).To(ContainSubstring("Invalid value for 'memory': 128"))
   101  	})
   102  
   103  	It("sets applications' health check timeouts", func() {
   104  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   105  			"applications": []interface{}{
   106  				map[interface{}]interface{}{
   107  					"name":    "bitcoin-miner",
   108  					"timeout": "360",
   109  				},
   110  			},
   111  		}))
   112  
   113  		apps, err := m.Applications()
   114  		Expect(err).NotTo(HaveOccurred())
   115  		Expect(*apps[0].HealthCheckTimeout).To(Equal(360))
   116  	})
   117  
   118  	It("allows boolean env var values", func() {
   119  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   120  			"env": generic.NewMap(map[interface{}]interface{}{
   121  				"bar": true,
   122  			}),
   123  		}))
   124  
   125  		_, err := m.Applications()
   126  		Expect(err).ToNot(HaveOccurred())
   127  	})
   128  
   129  	It("allows nil value for global env if env is present in the app", func() {
   130  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   131  			"env": nil,
   132  			"applications": []interface{}{
   133  				map[interface{}]interface{}{
   134  					"name": "bad app",
   135  					"env": map[interface{}]interface{}{
   136  						"foo": "bar",
   137  					},
   138  				},
   139  			},
   140  		}))
   141  
   142  		apps, err := m.Applications()
   143  		Expect(err).NotTo(HaveOccurred())
   144  		Expect(*apps[0].EnvironmentVars).To(Equal(map[string]interface{}{"foo": "bar"}))
   145  	})
   146  
   147  	It("does not allow nil value for env in application", func() {
   148  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   149  			"env": generic.NewMap(map[interface{}]interface{}{
   150  				"foo": "bar",
   151  			}),
   152  			"applications": []interface{}{
   153  				map[interface{}]interface{}{
   154  					"name": "bad app",
   155  					"env":  nil,
   156  				},
   157  			},
   158  		}))
   159  
   160  		_, err := m.Applications()
   161  		Expect(err).To(HaveOccurred())
   162  		Expect(err.Error()).To(ContainSubstring("env should not be null"))
   163  	})
   164  
   165  	It("does not allow nil values for environment variables", func() {
   166  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   167  			"env": generic.NewMap(map[interface{}]interface{}{
   168  				"bar": nil,
   169  			}),
   170  			"applications": []interface{}{
   171  				map[interface{}]interface{}{
   172  					"name": "bad app",
   173  				},
   174  			},
   175  		}))
   176  
   177  		_, err := m.Applications()
   178  		Expect(err).To(HaveOccurred())
   179  		Expect(err.Error()).To(ContainSubstring("env var 'bar' should not be null"))
   180  	})
   181  
   182  	It("returns an empty map when no env was present in the manifest", func() {
   183  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   184  			"applications": []interface{}{
   185  				map[interface{}]interface{}{"name": "no-env-vars"},
   186  			},
   187  		}))
   188  
   189  		apps, err := m.Applications()
   190  		Expect(err).NotTo(HaveOccurred())
   191  		Expect(*apps[0].EnvironmentVars).NotTo(BeNil())
   192  	})
   193  
   194  	It("allows applications to have absolute paths", func() {
   195  		if runtime.GOOS == "windows" {
   196  			m := NewManifest(`C:\some\path\manifest.yml`, generic.NewMap(map[interface{}]interface{}{
   197  				"applications": []interface{}{
   198  					map[interface{}]interface{}{
   199  						"path": `C:\another\path`,
   200  					},
   201  				},
   202  			}))
   203  
   204  			apps, err := m.Applications()
   205  			Expect(err).NotTo(HaveOccurred())
   206  			Expect(*apps[0].Path).To(Equal(`C:\another\path`))
   207  		} else {
   208  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   209  				"applications": []interface{}{
   210  					map[interface{}]interface{}{
   211  						"path": "/another/path-segment",
   212  					},
   213  				},
   214  			}))
   215  
   216  			apps, err := m.Applications()
   217  			Expect(err).NotTo(HaveOccurred())
   218  			Expect(*apps[0].Path).To(Equal("/another/path-segment"))
   219  		}
   220  	})
   221  
   222  	It("expands relative app paths based on the manifest's path", func() {
   223  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   224  			"applications": []interface{}{
   225  				map[interface{}]interface{}{
   226  					"path": "../another/path-segment",
   227  				},
   228  			},
   229  		}))
   230  
   231  		apps, err := m.Applications()
   232  		Expect(err).NotTo(HaveOccurred())
   233  		if runtime.GOOS == "windows" {
   234  			Expect(*apps[0].Path).To(Equal("\\some\\another\\path-segment"))
   235  		} else {
   236  			Expect(*apps[0].Path).To(Equal("/some/another/path-segment"))
   237  		}
   238  	})
   239  
   240  	It("returns errors when there are null values", func() {
   241  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   242  			"applications": []interface{}{
   243  				map[interface{}]interface{}{
   244  					"disk_quota":   nil,
   245  					"domain":       nil,
   246  					"host":         nil,
   247  					"name":         nil,
   248  					"path":         nil,
   249  					"stack":        nil,
   250  					"memory":       nil,
   251  					"instances":    nil,
   252  					"timeout":      nil,
   253  					"no-route":     nil,
   254  					"no-hostname":  nil,
   255  					"services":     nil,
   256  					"env":          nil,
   257  					"random-route": nil,
   258  				},
   259  			},
   260  		}))
   261  
   262  		_, err := m.Applications()
   263  		Expect(err).To(HaveOccurred())
   264  		errorSlice := strings.Split(err.Error(), "\n")
   265  		manifestKeys := []string{"disk_quota", "domain", "host", "name", "path", "stack",
   266  			"memory", "instances", "timeout", "no-route", "no-hostname", "services", "env", "random-route"}
   267  
   268  		for _, key := range manifestKeys {
   269  			Expect(errorSlice).To(ContainSubstrings([]string{key, "not be null"}))
   270  		}
   271  	})
   272  
   273  	It("returns errors when hosts/domains is not valid slice", func() {
   274  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   275  			"applications": []interface{}{
   276  				map[interface{}]interface{}{
   277  					"hosts":   "bad-value",
   278  					"domains": []interface{}{"val1", "val2", false, true},
   279  				},
   280  			},
   281  		}))
   282  
   283  		_, err := m.Applications()
   284  		Expect(err).To(HaveOccurred())
   285  		errorSlice := strings.Split(err.Error(), "\n")
   286  
   287  		Expect(errorSlice).To(ContainSubstrings([]string{"hosts", "to be a list of strings"}))
   288  		Expect(errorSlice).To(ContainSubstrings([]string{"domains", "to be a list of strings"}))
   289  	})
   290  
   291  	It("parses known manifest keys", func() {
   292  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   293  			"applications": []interface{}{
   294  				map[interface{}]interface{}{
   295  					"buildpack":         "my-buildpack",
   296  					"disk_quota":        "512M",
   297  					"domain":            "my-domain",
   298  					"domains":           []interface{}{"domain1.test", "domain2.test"},
   299  					"host":              "my-hostname",
   300  					"hosts":             []interface{}{"host-1", "host-2"},
   301  					"name":              "my-app-name",
   302  					"stack":             "my-stack",
   303  					"memory":            "256M",
   304  					"health-check-type": "none",
   305  					"instances":         1,
   306  					"timeout":           11,
   307  					"no-route":          true,
   308  					"no-hostname":       true,
   309  					"random-route":      true,
   310  					"docker": map[interface{}]interface{}{
   311  						"image": "docker.com:3333/docker/docker:some-tag",
   312  					},
   313  				},
   314  			},
   315  		}))
   316  
   317  		apps, err := m.Applications()
   318  		Expect(err).NotTo(HaveOccurred())
   319  		Expect(len(apps)).To(Equal(1))
   320  
   321  		Expect(*apps[0].BuildpackURL).To(Equal("my-buildpack"))
   322  		Expect(*apps[0].DiskQuota).To(Equal(int64(512)))
   323  		Expect(apps[0].Domains).To(ConsistOf([]string{"domain1.test", "domain2.test", "my-domain"}))
   324  		Expect(apps[0].Hosts).To(ConsistOf([]string{"host-1", "host-2", "my-hostname"}))
   325  		Expect(*apps[0].Name).To(Equal("my-app-name"))
   326  		Expect(*apps[0].StackName).To(Equal("my-stack"))
   327  		Expect(*apps[0].HealthCheckType).To(Equal("none"))
   328  		Expect(*apps[0].Memory).To(Equal(int64(256)))
   329  		Expect(*apps[0].InstanceCount).To(Equal(1))
   330  		Expect(*apps[0].HealthCheckTimeout).To(Equal(11))
   331  		Expect(apps[0].NoRoute).To(BeTrue())
   332  		Expect(*apps[0].NoHostname).To(BeTrue())
   333  		Expect(apps[0].UseRandomRoute).To(BeTrue())
   334  		Expect(*apps[0].DockerImage).To(Equal("docker.com:3333/docker/docker:some-tag"))
   335  	})
   336  
   337  	Context("when the health-check-type is 'http'", func() {
   338  		Context("when health-check-http-endpoint IS provided", func() {
   339  			It("sets http-health-check-endpoint to the provided endpoint", func() {
   340  				m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   341  					"applications": []interface{}{
   342  						map[interface{}]interface{}{
   343  							"health-check-type":          "http",
   344  							"health-check-http-endpoint": "/some-endpoint",
   345  						},
   346  					},
   347  				}))
   348  
   349  				apps, err := m.Applications()
   350  				Expect(err).NotTo(HaveOccurred())
   351  				Expect(len(apps)).To(Equal(1))
   352  
   353  				Expect(*apps[0].HealthCheckType).To(Equal("http"))
   354  				Expect(*apps[0].HealthCheckHTTPEndpoint).To(Equal("/some-endpoint"))
   355  			})
   356  		})
   357  	})
   358  
   359  	It("removes duplicated values in 'hosts' and 'domains'", func() {
   360  		m := NewManifest("/some/path", generic.NewMap(map[interface{}]interface{}{
   361  			"applications": []interface{}{
   362  				map[interface{}]interface{}{
   363  					"domain":  "my-domain",
   364  					"domains": []interface{}{"my-domain", "domain1.test", "domain1.test", "domain2.test"},
   365  					"host":    "my-hostname",
   366  					"hosts":   []interface{}{"my-hostname", "host-1", "host-1", "host-2"},
   367  					"name":    "my-app-name",
   368  				},
   369  			},
   370  		}))
   371  
   372  		apps, err := m.Applications()
   373  		Expect(err).NotTo(HaveOccurred())
   374  		Expect(len(apps)).To(Equal(1))
   375  
   376  		Expect(len(apps[0].Domains)).To(Equal(3))
   377  		Expect(apps[0].Domains).To(ConsistOf([]string{"my-domain", "domain1.test", "domain2.test"}))
   378  		Expect(len(apps[0].Hosts)).To(Equal(3))
   379  		Expect(apps[0].Hosts).To(ConsistOf([]string{"my-hostname", "host-1", "host-2"}))
   380  	})
   381  
   382  	It("sets the command and buildpack to blank when their values are null in the manifest", func() {
   383  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   384  			"applications": []interface{}{
   385  				generic.NewMap(map[interface{}]interface{}{
   386  					"buildpack": nil,
   387  					"command":   nil,
   388  				}),
   389  			},
   390  		}))
   391  
   392  		apps, err := m.Applications()
   393  		Expect(err).NotTo(HaveOccurred())
   394  		Expect(*apps[0].Command).To(Equal(""))
   395  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   396  	})
   397  
   398  	It("sets the command and buildpack to blank when their values are 'default' in the manifest", func() {
   399  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   400  			"applications": []interface{}{
   401  				generic.NewMap(map[interface{}]interface{}{
   402  					"command":   "default",
   403  					"buildpack": "default",
   404  				}),
   405  			},
   406  		}))
   407  
   408  		apps, err := m.Applications()
   409  		Expect(err).NotTo(HaveOccurred())
   410  		Expect(*apps[0].Command).To(Equal(""))
   411  		Expect(*apps[0].BuildpackURL).To(Equal(""))
   412  	})
   413  
   414  	It("does not set the start command when the manifest doesn't have the 'command' key", func() {
   415  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   416  			"applications": []interface{}{
   417  				map[interface{}]interface{}{},
   418  			},
   419  		}))
   420  
   421  		apps, err := m.Applications()
   422  		Expect(err).NotTo(HaveOccurred())
   423  		Expect(apps[0].Command).To(BeNil())
   424  	})
   425  
   426  	It("can build the applications multiple times", func() {
   427  		m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   428  			"memory": "254m",
   429  			"applications": []interface{}{
   430  				map[interface{}]interface{}{
   431  					"name": "bitcoin-miner",
   432  				},
   433  				map[interface{}]interface{}{
   434  					"name": "bitcoin-miner",
   435  				},
   436  			},
   437  		}))
   438  
   439  		apps1, err := m.Applications()
   440  		Expect(err).NotTo(HaveOccurred())
   441  
   442  		apps2, err := m.Applications()
   443  		Expect(err).NotTo(HaveOccurred())
   444  		Expect(apps1).To(Equal(apps2))
   445  	})
   446  
   447  	Context("parsing env vars", func() {
   448  		It("handles values that are not strings", func() {
   449  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   450  				"applications": []interface{}{
   451  					generic.NewMap(map[interface{}]interface{}{
   452  						"env": map[interface{}]interface{}{
   453  							"string-key":      "value",
   454  							"int-key":         1,
   455  							"float-key":       11.1,
   456  							"large-int-key":   123456789,
   457  							"large-float-key": 123456789.12345678,
   458  							"bool-key":        false,
   459  						},
   460  					}),
   461  				},
   462  			}))
   463  
   464  			app, err := m.Applications()
   465  			Expect(err).NotTo(HaveOccurred())
   466  
   467  			Expect((*app[0].EnvironmentVars)["string-key"]).To(Equal("value"))
   468  			Expect((*app[0].EnvironmentVars)["int-key"]).To(Equal("1"))
   469  			Expect((*app[0].EnvironmentVars)["float-key"]).To(Equal("11.1"))
   470  			Expect((*app[0].EnvironmentVars)["large-int-key"]).To(Equal("123456789"))
   471  			Expect((*app[0].EnvironmentVars)["large-float-key"]).To(Equal("123456789.12345678"))
   472  			Expect((*app[0].EnvironmentVars)["bool-key"]).To(Equal("false"))
   473  		})
   474  	})
   475  
   476  	Context("parsing services", func() {
   477  		It("can read a list of service instance names", func() {
   478  			m := NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   479  				"services": []interface{}{"service-1", "service-2"},
   480  			}))
   481  
   482  			app, err := m.Applications()
   483  			Expect(err).NotTo(HaveOccurred())
   484  
   485  			Expect(app[0].ServicesToBind).To(Equal([]string{"service-1", "service-2"}))
   486  		})
   487  	})
   488  
   489  	Context("when routes are provided", func() {
   490  		var manifest *manifest.Manifest
   491  
   492  		Context("when passed 'routes'", func() {
   493  			Context("valid 'routes'", func() {
   494  				BeforeEach(func() {
   495  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   496  						"applications": []interface{}{
   497  							generic.NewMap(map[interface{}]interface{}{
   498  								"routes": []interface{}{
   499  									map[interface{}]interface{}{"route": "route1.example.com"},
   500  									map[interface{}]interface{}{"route": "route2.example.com"},
   501  								},
   502  							}),
   503  						},
   504  					}))
   505  				})
   506  
   507  				It("parses routes into app params", func() {
   508  					apps, err := manifest.Applications()
   509  					Expect(err).NotTo(HaveOccurred())
   510  					Expect(apps).To(HaveLen(1))
   511  
   512  					routes := apps[0].Routes
   513  					Expect(routes).To(HaveLen(2))
   514  					Expect(routes[0].Route).To(Equal("route1.example.com"))
   515  					Expect(routes[1].Route).To(Equal("route2.example.com"))
   516  				})
   517  			})
   518  
   519  			Context("invalid 'routes'", func() {
   520  				Context("'routes' is formatted incorrectly", func() {
   521  					BeforeEach(func() {
   522  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   523  							"applications": []interface{}{
   524  								generic.NewMap(map[interface{}]interface{}{
   525  									"routes": []string{},
   526  								}),
   527  							},
   528  						}))
   529  					})
   530  
   531  					It("errors out", func() {
   532  						_, err := manifest.Applications()
   533  						Expect(err).To(HaveOccurred())
   534  						Expect(err.Error()).To(MatchRegexp("should be a list"))
   535  					})
   536  				})
   537  
   538  				Context("an individual 'route' is formatted incorrectly", func() {
   539  					BeforeEach(func() {
   540  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   541  							"applications": []interface{}{
   542  								generic.NewMap(map[interface{}]interface{}{
   543  									"routes": []interface{}{
   544  										map[interface{}]interface{}{"routef": "route1.example.com"},
   545  									},
   546  								}),
   547  							},
   548  						}))
   549  					})
   550  
   551  					It("parses routes into app params", func() {
   552  						_, err := manifest.Applications()
   553  						Expect(err).To(HaveOccurred())
   554  						Expect(err.Error()).To(MatchRegexp("each route in 'routes' must have a 'route' property"))
   555  					})
   556  				})
   557  			})
   558  		})
   559  
   560  		Context("when there are no routes", func() {
   561  			BeforeEach(func() {
   562  				manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   563  					"applications": []interface{}{
   564  						generic.NewMap(map[interface{}]interface{}{
   565  							"buildpack": nil,
   566  							"command":   "echo banana",
   567  						}),
   568  					},
   569  				}))
   570  			})
   571  
   572  			It("sets routes to be nil", func() {
   573  				apps, err := manifest.Applications()
   574  				Expect(err).NotTo(HaveOccurred())
   575  				Expect(apps).To(HaveLen(1))
   576  				Expect(apps[0].Routes).To(BeNil())
   577  			})
   578  		})
   579  
   580  		Context("when no-hostname is not specified in the manifest", func() {
   581  			BeforeEach(func() {
   582  				manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   583  					"applications": []interface{}{
   584  						generic.NewMap(map[interface{}]interface{}{
   585  							"buildpack": nil,
   586  							"command":   "echo banana",
   587  						}),
   588  					},
   589  				}))
   590  			})
   591  
   592  			It("sets no-hostname to be nil", func() {
   593  				apps, err := manifest.Applications()
   594  				Expect(err).NotTo(HaveOccurred())
   595  				Expect(apps).To(HaveLen(1))
   596  				Expect(apps[0].NoHostname).To(BeNil())
   597  			})
   598  		})
   599  
   600  		Context("when no-hostname is specified in the manifest", func() {
   601  			Context("and it is set to true", func() {
   602  				Context("and the value is a boolean", func() {
   603  					BeforeEach(func() {
   604  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   605  							"applications": []interface{}{
   606  								generic.NewMap(map[interface{}]interface{}{
   607  									"buildpack":   nil,
   608  									"command":     "echo banana",
   609  									"no-hostname": true,
   610  								}),
   611  							},
   612  						}))
   613  					})
   614  
   615  					It("sets no-hostname to be true", func() {
   616  						apps, err := manifest.Applications()
   617  						Expect(err).NotTo(HaveOccurred())
   618  						Expect(apps).To(HaveLen(1))
   619  						Expect(*apps[0].NoHostname).To(BeTrue())
   620  					})
   621  				})
   622  				Context("and the value is a string", func() {
   623  					BeforeEach(func() {
   624  						manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   625  							"applications": []interface{}{
   626  								generic.NewMap(map[interface{}]interface{}{
   627  									"buildpack":   nil,
   628  									"command":     "echo banana",
   629  									"no-hostname": "true",
   630  								}),
   631  							},
   632  						}))
   633  					})
   634  
   635  					It("sets no-hostname to be true", func() {
   636  						apps, err := manifest.Applications()
   637  						Expect(err).NotTo(HaveOccurred())
   638  						Expect(apps).To(HaveLen(1))
   639  						Expect(*apps[0].NoHostname).To(BeTrue())
   640  					})
   641  				})
   642  			})
   643  			Context("and it is set to false", func() {
   644  				BeforeEach(func() {
   645  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   646  						"applications": []interface{}{
   647  							generic.NewMap(map[interface{}]interface{}{
   648  								"buildpack":   nil,
   649  								"command":     "echo banana",
   650  								"no-hostname": false,
   651  							}),
   652  						},
   653  					}))
   654  				})
   655  				It("sets no-hostname to be false", func() {
   656  					apps, err := manifest.Applications()
   657  					Expect(err).NotTo(HaveOccurred())
   658  					Expect(apps).To(HaveLen(1))
   659  					Expect(*apps[0].NoHostname).To(BeFalse())
   660  				})
   661  			})
   662  		})
   663  	})
   664  
   665  	Context("when the 'docker' property is not specified", func() {
   666  		var manifest *manifest.Manifest
   667  
   668  		BeforeEach(func() {
   669  			manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   670  				"applications": []interface{}{
   671  					generic.NewMap(map[interface{}]interface{}{}),
   672  				},
   673  			}))
   674  		})
   675  
   676  		It("returns a parsed manifest that does not contain any docker fields", func() {
   677  			apps, err := manifest.Applications()
   678  			Expect(err).NotTo(HaveOccurred())
   679  			Expect(apps).To(HaveLen(1))
   680  			Expect(apps[0].DockerImage).To(BeNil())
   681  			Expect(apps[0].DockerUsername).To(BeNil())
   682  		})
   683  	})
   684  
   685  	Context("when the 'docker' property is specified but contains no properties", func() {
   686  		var manifest *manifest.Manifest
   687  
   688  		BeforeEach(func() {
   689  			manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   690  				"applications": []interface{}{
   691  					generic.NewMap(map[interface{}]interface{}{
   692  						"docker": map[interface{}]interface{}{},
   693  					}),
   694  				},
   695  			}))
   696  		})
   697  
   698  		It("returns a parsed manifest that does not contain any docker fields", func() {
   699  			apps, err := manifest.Applications()
   700  			Expect(err).NotTo(HaveOccurred())
   701  			Expect(apps).To(HaveLen(1))
   702  			Expect(apps[0].DockerImage).To(BeNil())
   703  			Expect(apps[0].DockerUsername).To(BeNil())
   704  		})
   705  	})
   706  
   707  	Context("when the 'docker' property is specified", func() {
   708  		var manifest *manifest.Manifest
   709  
   710  		Context("when 'docker' contains an 'image' property", func() {
   711  			Context("when the 'image' property is not a string", func() {
   712  				BeforeEach(func() {
   713  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   714  						"applications": []interface{}{
   715  							generic.NewMap(map[interface{}]interface{}{
   716  								"docker": map[interface{}]interface{}{
   717  									"image": []interface{}{},
   718  								},
   719  							}),
   720  						},
   721  					}))
   722  				})
   723  
   724  				It("returns an error", func() {
   725  					_, err := manifest.Applications()
   726  					Expect(err.Error()).To(MatchRegexp("'docker.image' must be a string"))
   727  				})
   728  			})
   729  
   730  			Context("when the 'image' property is a string", func() {
   731  				BeforeEach(func() {
   732  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   733  						"applications": []interface{}{
   734  							generic.NewMap(map[interface{}]interface{}{
   735  								"docker": map[interface{}]interface{}{
   736  									"image": "docker.com:3333/docker/docker:tag",
   737  								},
   738  							}),
   739  						},
   740  					}))
   741  				})
   742  
   743  				It("sets DockerImage to the 'image' property value", func() {
   744  					apps, err := manifest.Applications()
   745  					Expect(err).NotTo(HaveOccurred())
   746  					Expect(apps).To(HaveLen(1))
   747  					Expect(*apps[0].DockerImage).To(Equal("docker.com:3333/docker/docker:tag"))
   748  				})
   749  			})
   750  		})
   751  
   752  		Context("when 'docker' contains a 'username' property", func() {
   753  			Context("when 'username' is not a string", func() {
   754  				BeforeEach(func() {
   755  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   756  						"applications": []interface{}{
   757  							generic.NewMap(map[interface{}]interface{}{
   758  								"docker": map[interface{}]interface{}{
   759  									"username": []interface{}{},
   760  								},
   761  							}),
   762  						},
   763  					}))
   764  				})
   765  
   766  				It("returns an error", func() {
   767  					_, err := manifest.Applications()
   768  					Expect(err.Error()).To(MatchRegexp("'docker.username' must be a string"))
   769  				})
   770  			})
   771  
   772  			Context("when 'username' is a string", func() {
   773  				BeforeEach(func() {
   774  					manifest = NewManifest("/some/path/manifest.yml", generic.NewMap(map[interface{}]interface{}{
   775  						"applications": []interface{}{
   776  							generic.NewMap(map[interface{}]interface{}{
   777  								"docker": map[interface{}]interface{}{
   778  									"username": "some-user",
   779  								},
   780  							}),
   781  						},
   782  					}))
   783  				})
   784  
   785  				It("sets DockerUsername to the 'username' property value", func() {
   786  					apps, err := manifest.Applications()
   787  					Expect(err).NotTo(HaveOccurred())
   788  					Expect(apps).To(HaveLen(1))
   789  					Expect(*apps[0].DockerUsername).To(Equal("some-user"))
   790  				})
   791  			})
   792  		})
   793  	})
   794  })