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