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  })