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

     1  package v6manifestparser_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"code.cloudfoundry.org/cli/cf/util/testhelpers/matchers"
    11  	. "code.cloudfoundry.org/cli/util/v6manifestparser"
    12  	"github.com/cloudfoundry/bosh-cli/director/template"
    13  	. "github.com/onsi/ginkgo"
    14  	. "github.com/onsi/gomega"
    15  )
    16  
    17  var _ = Describe("Parser", func() {
    18  	var parser *Parser
    19  
    20  	BeforeEach(func() {
    21  		parser = NewParser()
    22  	})
    23  
    24  	Describe("NewParser", func() {
    25  		It("returns a parser", func() {
    26  			Expect(parser).ToNot(BeNil())
    27  		})
    28  	})
    29  
    30  	Describe("AppNames", func() {
    31  		When("given a valid manifest file", func() {
    32  			BeforeEach(func() {
    33  				parser.Applications = []Application{
    34  					{ApplicationModel: ApplicationModel{Name: "app-1"}, FullUnmarshalledApplication: nil},
    35  					{ApplicationModel: ApplicationModel{Name: "app-2"}, FullUnmarshalledApplication: nil}}
    36  			})
    37  
    38  			It("gets the app names", func() {
    39  				appNames := parser.AppNames()
    40  				Expect(appNames).To(ConsistOf("app-1", "app-2"))
    41  			})
    42  		})
    43  	})
    44  
    45  	Describe("ContainsManifest", func() {
    46  		var (
    47  			pathToManifest string
    48  		)
    49  
    50  		BeforeEach(func() {
    51  			tempFile, err := ioutil.TempFile("", "contains-manifest-test")
    52  			Expect(err).ToNot(HaveOccurred())
    53  			pathToManifest = tempFile.Name()
    54  			Expect(tempFile.Close()).ToNot(HaveOccurred())
    55  		})
    56  
    57  		AfterEach(func() {
    58  			err := os.RemoveAll(pathToManifest)
    59  			Expect(err).ToNot(HaveOccurred())
    60  		})
    61  
    62  		Context("when the manifest is parsed successfully", func() {
    63  			BeforeEach(func() {
    64  				rawManifest := []byte(`---
    65  applications:
    66  - name: spark
    67  `)
    68  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
    69  				Expect(err).ToNot(HaveOccurred())
    70  
    71  				err = parser.InterpolateAndParse(pathToManifest, nil, nil, "")
    72  				Expect(err).ToNot(HaveOccurred())
    73  			})
    74  
    75  			It("returns true", func() {
    76  				Expect(parser.ContainsManifest()).To(BeTrue())
    77  			})
    78  		})
    79  
    80  		Context("when the manifest is not parsed successfully", func() {
    81  			BeforeEach(func() {
    82  				rawManifest := []byte(`---
    83  applications:
    84  `)
    85  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
    86  				Expect(err).ToNot(HaveOccurred())
    87  
    88  				err = parser.InterpolateAndParse(pathToManifest, nil, nil, "")
    89  				Expect(err).To(HaveOccurred())
    90  			})
    91  
    92  			It("returns false", func() {
    93  				Expect(parser.ContainsManifest()).To(BeFalse())
    94  			})
    95  		})
    96  
    97  		Context("when the manifest has not been parsed", func() {
    98  			It("returns false", func() {
    99  				Expect(parser.ContainsManifest()).To(BeFalse())
   100  			})
   101  		})
   102  	})
   103  
   104  	Describe("ContainsMultipleApps", func() {
   105  		When("given a valid manifest file with multiple apps", func() {
   106  			BeforeEach(func() {
   107  				parser.Applications = []Application{
   108  					{ApplicationModel: ApplicationModel{Name: "app-1"}, FullUnmarshalledApplication: nil},
   109  					{ApplicationModel: ApplicationModel{Name: "app-2"}, FullUnmarshalledApplication: nil}}
   110  			})
   111  
   112  			It("returns true", func() {
   113  				Expect(parser.ContainsMultipleApps()).To(BeTrue())
   114  			})
   115  		})
   116  
   117  		When("given a valid manifest file with a single app", func() {
   118  			BeforeEach(func() {
   119  				parser.Applications = []Application{{ApplicationModel: ApplicationModel{Name: "app-1"}, FullUnmarshalledApplication: nil}}
   120  			})
   121  
   122  			It("returns false", func() {
   123  				Expect(parser.ContainsMultipleApps()).To(BeFalse())
   124  			})
   125  		})
   126  	})
   127  
   128  	Describe("ContainsPrivateDockerImages", func() {
   129  		When("the manifest contains a docker image", func() {
   130  			When("the image is public", func() {
   131  				BeforeEach(func() {
   132  					parser.Applications = []Application{
   133  						{ApplicationModel: ApplicationModel{Name: "app-1", Docker: &Docker{Image: "image-1"}}, FullUnmarshalledApplication: nil},
   134  						{ApplicationModel: ApplicationModel{Name: "app-2", Docker: &Docker{Image: "image-2"}}, FullUnmarshalledApplication: nil}}
   135  				})
   136  
   137  				It("returns false", func() {
   138  					Expect(parser.ContainsPrivateDockerImages()).To(BeFalse())
   139  				})
   140  			})
   141  
   142  			When("the image is private", func() {
   143  				BeforeEach(func() {
   144  					parser.Applications = []Application{
   145  						{ApplicationModel: ApplicationModel{Name: "app-1", Docker: &Docker{Image: "image-1"}}},
   146  						{ApplicationModel: ApplicationModel{Name: "app-2", Docker: &Docker{Image: "image-2", Username: "user"}}},
   147  					}
   148  				})
   149  
   150  				It("returns true", func() {
   151  					Expect(parser.ContainsPrivateDockerImages()).To(BeTrue())
   152  				})
   153  			})
   154  		})
   155  
   156  		When("the manifest does not contain a docker image", func() {
   157  			BeforeEach(func() {
   158  				parser.Applications = []Application{
   159  					{ApplicationModel: ApplicationModel{Name: "app-1"}},
   160  					{ApplicationModel: ApplicationModel{Name: "app-2"}},
   161  				}
   162  			})
   163  
   164  			It("returns false", func() {
   165  				Expect(parser.ContainsPrivateDockerImages()).To(BeFalse())
   166  			})
   167  		})
   168  	})
   169  
   170  	Describe("InterpolateAndParse", func() {
   171  		var (
   172  			pathToManifest   string
   173  			pathsToVarsFiles []string
   174  			vars             []template.VarKV
   175  			appName          string
   176  
   177  			executeErr error
   178  
   179  			rawManifest []byte
   180  		)
   181  
   182  		BeforeEach(func() {
   183  			tempFile, err := ioutil.TempFile("", "manifest-test-")
   184  			Expect(err).ToNot(HaveOccurred())
   185  			Expect(tempFile.Close()).ToNot(HaveOccurred())
   186  			pathToManifest = tempFile.Name()
   187  			vars = nil
   188  			appName = ""
   189  
   190  			pathsToVarsFiles = nil
   191  		})
   192  
   193  		AfterEach(func() {
   194  			Expect(os.RemoveAll(pathToManifest)).ToNot(HaveOccurred())
   195  			for _, path := range pathsToVarsFiles {
   196  				Expect(os.RemoveAll(path)).ToNot(HaveOccurred())
   197  			}
   198  		})
   199  
   200  		JustBeforeEach(func() {
   201  			executeErr = parser.InterpolateAndParse(pathToManifest, pathsToVarsFiles, vars, appName)
   202  		})
   203  
   204  		When("the manifest does *not* need interpolation", func() {
   205  			BeforeEach(func() {
   206  				rawManifest = []byte(`---
   207  applications:
   208  - name: spark
   209    memory: 1G
   210    instances: 2
   211  - name: flame
   212    memory: 1G
   213    instances: 2
   214  `)
   215  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   216  				Expect(err).ToNot(HaveOccurred())
   217  			})
   218  
   219  			It("parses the manifest properly", func() {
   220  				Expect(executeErr).ToNot(HaveOccurred())
   221  
   222  				Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   223  				Expect(parser.GetPathToManifest()).To(Equal(pathToManifest))
   224  				Expect(parser.FullRawManifest()).To(MatchYAML(rawManifest))
   225  			})
   226  		})
   227  
   228  		When("the manifest contains variables that need interpolation", func() {
   229  			BeforeEach(func() {
   230  				rawManifest = []byte(`---
   231  applications:
   232  - name: ((var1))
   233  - name: ((var2))
   234  `)
   235  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   236  				Expect(err).ToNot(HaveOccurred())
   237  			})
   238  
   239  			When("only vars files are provided", func() {
   240  				var (
   241  					varsDir string
   242  				)
   243  
   244  				BeforeEach(func() {
   245  					var err error
   246  					varsDir, err = ioutil.TempDir("", "vars-test")
   247  					Expect(err).ToNot(HaveOccurred())
   248  
   249  					varsFilePath1 := filepath.Join(varsDir, "vars-1")
   250  					err = ioutil.WriteFile(varsFilePath1, []byte("var1: spark"), 0666)
   251  					Expect(err).ToNot(HaveOccurred())
   252  
   253  					varsFilePath2 := filepath.Join(varsDir, "vars-2")
   254  					err = ioutil.WriteFile(varsFilePath2, []byte("var2: flame"), 0666)
   255  					Expect(err).ToNot(HaveOccurred())
   256  
   257  					pathsToVarsFiles = append(pathsToVarsFiles, varsFilePath1, varsFilePath2)
   258  				})
   259  
   260  				AfterEach(func() {
   261  					Expect(os.RemoveAll(varsDir)).ToNot(HaveOccurred())
   262  				})
   263  
   264  				When("multiple values for the same variable(s) are provided", func() {
   265  					BeforeEach(func() {
   266  						varsFilePath1 := filepath.Join(varsDir, "vars-1")
   267  						err := ioutil.WriteFile(varsFilePath1, []byte("var1: garbageapp\nvar1: spark\nvar2: doesn't matter"), 0666)
   268  						Expect(err).ToNot(HaveOccurred())
   269  
   270  						varsFilePath2 := filepath.Join(varsDir, "vars-2")
   271  						err = ioutil.WriteFile(varsFilePath2, []byte("var2: flame"), 0666)
   272  						Expect(err).ToNot(HaveOccurred())
   273  
   274  						pathsToVarsFiles = append(pathsToVarsFiles, varsFilePath1, varsFilePath2)
   275  					})
   276  
   277  					It("interpolates the placeholder values", func() {
   278  						Expect(executeErr).ToNot(HaveOccurred())
   279  						Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   280  					})
   281  				})
   282  
   283  				When("the provided files exists and contain valid yaml", func() {
   284  					It("interpolates the placeholder values", func() {
   285  						Expect(executeErr).ToNot(HaveOccurred())
   286  						Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   287  					})
   288  				})
   289  
   290  				When("a variable in the manifest is not provided in the vars file", func() {
   291  					BeforeEach(func() {
   292  						varsFilePath := filepath.Join(varsDir, "vars-1")
   293  						err := ioutil.WriteFile(varsFilePath, []byte("notvar: foo"), 0666)
   294  						Expect(err).ToNot(HaveOccurred())
   295  
   296  						pathsToVarsFiles = []string{varsFilePath}
   297  					})
   298  
   299  					It("returns an error", func() {
   300  						Expect(executeErr.Error()).To(Equal("Expected to find variables: var1, var2"))
   301  					})
   302  				})
   303  
   304  				When("the provided file path does not exist", func() {
   305  					BeforeEach(func() {
   306  						pathsToVarsFiles = []string{"garbagepath"}
   307  					})
   308  
   309  					It("returns an error", func() {
   310  						Expect(executeErr).To(HaveOccurred())
   311  						Expect(os.IsNotExist(executeErr)).To(BeTrue())
   312  					})
   313  				})
   314  
   315  				When("the provided file is not a valid yaml file", func() {
   316  					BeforeEach(func() {
   317  						varsFilePath := filepath.Join(varsDir, "vars-1")
   318  						err := ioutil.WriteFile(varsFilePath, []byte(": bad"), 0666)
   319  						Expect(err).ToNot(HaveOccurred())
   320  
   321  						pathsToVarsFiles = []string{varsFilePath}
   322  					})
   323  
   324  					It("returns an error", func() {
   325  						Expect(executeErr).To(HaveOccurred())
   326  						Expect(executeErr).To(MatchError(InvalidYAMLError{
   327  							Err: errors.New("yaml: did not find expected key"),
   328  						}))
   329  					})
   330  				})
   331  			})
   332  
   333  			When("only vars are provided", func() {
   334  				BeforeEach(func() {
   335  					vars = []template.VarKV{
   336  						{Name: "var1", Value: "spark"},
   337  						{Name: "var2", Value: "flame"},
   338  					}
   339  				})
   340  
   341  				It("interpolates the placeholder values", func() {
   342  					Expect(executeErr).ToNot(HaveOccurred())
   343  					Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   344  				})
   345  			})
   346  
   347  			When("vars and vars files are provided", func() {
   348  				var varsFilePath string
   349  				BeforeEach(func() {
   350  					tmp, err := ioutil.TempFile("", "util-manifest-varsilfe")
   351  					Expect(err).NotTo(HaveOccurred())
   352  					Expect(tmp.Close()).NotTo(HaveOccurred())
   353  
   354  					varsFilePath = tmp.Name()
   355  					err = ioutil.WriteFile(varsFilePath, []byte("var1: spark\nvar2: 12345"), 0666)
   356  					Expect(err).ToNot(HaveOccurred())
   357  
   358  					pathsToVarsFiles = []string{varsFilePath}
   359  					vars = []template.VarKV{
   360  						{Name: "var2", Value: "flame"},
   361  					}
   362  				})
   363  
   364  				AfterEach(func() {
   365  					Expect(os.RemoveAll(varsFilePath)).ToNot(HaveOccurred())
   366  				})
   367  
   368  				It("interpolates the placeholder values, prioritizing the vars flag", func() {
   369  					Expect(executeErr).ToNot(HaveOccurred())
   370  					Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   371  				})
   372  			})
   373  		})
   374  
   375  		When("invalid yaml is passed", func() {
   376  			BeforeEach(func() {
   377  				rawManifest = []byte("\t\t")
   378  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   379  				Expect(err).ToNot(HaveOccurred())
   380  			})
   381  
   382  			It("parses the manifest properly", func() {
   383  				Expect(executeErr).To(HaveOccurred())
   384  			})
   385  		})
   386  
   387  		When("passing an app name override", func() {
   388  			BeforeEach(func() {
   389  				appName = "mashed-potato"
   390  			})
   391  
   392  			When("there is only one app", func() {
   393  				When("the app has a name", func() {
   394  					BeforeEach(func() {
   395  						rawManifest = []byte(`---
   396  applications:
   397  - name: spark
   398    instances: 2
   399  `)
   400  						err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   401  						Expect(err).ToNot(HaveOccurred())
   402  					})
   403  
   404  					It("sets its name in the raw manifest", func() {
   405  						Expect(parser.FullRawManifest()).To(MatchYAML(`---
   406  applications:
   407  - name: mashed-potato
   408    instances: 2
   409  `))
   410  					})
   411  
   412  					It("sets its name in the application object", func() {
   413  						Expect(parser.Applications).To(HaveLen(1))
   414  						Expect(parser.Applications[0].Name).To(Equal("mashed-potato"))
   415  					})
   416  				})
   417  
   418  				When("the app does *not* have a name", func() {
   419  					BeforeEach(func() {
   420  						rawManifest = []byte(`---
   421  applications:
   422  - instances: 2
   423  `)
   424  						err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   425  						Expect(err).ToNot(HaveOccurred())
   426  					})
   427  
   428  					It("sets its name in the raw manifest", func() {
   429  						Expect(parser.FullRawManifest()).To(MatchYAML(`---
   430  applications:
   431  - name: mashed-potato
   432    instances: 2
   433  `))
   434  					})
   435  
   436  					It("sets its name in the application object", func() {
   437  						Expect(parser.Applications).To(HaveLen(1))
   438  						Expect(parser.Applications[0].Name).To(Equal("mashed-potato"))
   439  					})
   440  				})
   441  			})
   442  
   443  			When("there are multiple apps", func() {
   444  				var (
   445  					app1FullPath string
   446  					app2FullPath string
   447  				)
   448  
   449  				BeforeEach(func() {
   450  					manifestDir := filepath.Dir(pathToManifest)
   451  					app1FullPath = filepath.Join(manifestDir, "app1")
   452  					app2FullPath = filepath.Join(manifestDir, "app2")
   453  
   454  					err := os.MkdirAll(app1FullPath, 0777)
   455  					Expect(err).ToNot(HaveOccurred())
   456  					err = os.MkdirAll(app2FullPath, 0777)
   457  					Expect(err).ToNot(HaveOccurred())
   458  
   459  					rawManifest = []byte(`---
   460  applications:
   461  - name: app-1
   462    instances: 2
   463    path: ./app1
   464  - name: app-2
   465    instances: 5
   466    path: ./app2
   467  `)
   468  					err = ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   469  					Expect(err).ToNot(HaveOccurred())
   470  				})
   471  
   472  				When("the override matches an app", func() {
   473  					BeforeEach(func() {
   474  						appName = "app-2"
   475  					})
   476  
   477  					It("keeps only the matching app in the raw manifest", func() {
   478  						Expect(parser.FullRawManifest()).To(MatchYAML(`---
   479  applications:
   480  - instances: 5
   481    name: app-2
   482    path: ./app2
   483  `))
   484  					})
   485  
   486  					It("keeps only the matching app in the applications list", func() {
   487  						Expect(parser.Applications).To(HaveLen(1))
   488  						Expect(parser.Applications[0].Name).To(Equal("app-2"))
   489  						Expect(parser.Applications[0].Path).To(matchers.MatchPath(app2FullPath))
   490  					})
   491  				})
   492  
   493  				When("the override does *not* match an app", func() {
   494  					BeforeEach(func() {
   495  						appName = "does-not-exist"
   496  					})
   497  
   498  					It("returns an error", func() {
   499  						Expect(executeErr).To(MatchError(AppNotInManifestError{Name: "does-not-exist"}))
   500  					})
   501  				})
   502  			})
   503  		})
   504  	})
   505  
   506  	Describe("RawAppManifest", func() {
   507  		var (
   508  			rawAppManifest []byte
   509  			appName        string
   510  			executeErr     error
   511  			rawManifest    []byte
   512  			pathToManifest string
   513  			tmpMyPath      string
   514  		)
   515  
   516  		BeforeEach(func() {
   517  			var err error
   518  
   519  			appName = "spark"
   520  
   521  			tmpMyPath, err = ioutil.TempDir("", "")
   522  			Expect(err).ToNot(HaveOccurred())
   523  
   524  			rawManifest = []byte(fmt.Sprintf(`---
   525  applications:
   526  - name: spark
   527    memory: 1G
   528    instances: 2
   529    docker:
   530      username: experiment
   531    path: %s
   532  - name: flame
   533    memory: 1G
   534    instances: 2
   535    docker:
   536      username: experiment
   537  `, tmpMyPath))
   538  
   539  		})
   540  
   541  		JustBeforeEach(func() {
   542  			tempFile, err := ioutil.TempFile("", "manifest-test-")
   543  			Expect(err).ToNot(HaveOccurred())
   544  			Expect(tempFile.Close()).ToNot(HaveOccurred())
   545  			pathToManifest = tempFile.Name()
   546  			err = ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   547  			Expect(err).ToNot(HaveOccurred())
   548  			err = parser.InterpolateAndParse(pathToManifest, nil, nil, "")
   549  			Expect(err).ToNot(HaveOccurred())
   550  			rawAppManifest, executeErr = parser.RawAppManifest(appName)
   551  		})
   552  
   553  		AfterEach(func() {
   554  			err := os.RemoveAll(pathToManifest)
   555  			Expect(err).ToNot(HaveOccurred())
   556  		})
   557  
   558  		When("marshaling does not error", func() {
   559  			It("returns just the app's manifest", func() {
   560  				Expect(executeErr).ToNot(HaveOccurred())
   561  				Expect(string(rawAppManifest)).To(MatchYAML(fmt.Sprintf(`applications:
   562  - name: spark
   563    memory: 1G
   564    instances: 2
   565    docker:
   566      username: experiment
   567    path: %s`, tmpMyPath)))
   568  			})
   569  		})
   570  
   571  		When("The app is not present", func() {
   572  			BeforeEach(func() {
   573  				appName = "not-here"
   574  			})
   575  
   576  			It("returns an error", func() {
   577  				Expect(executeErr).To(MatchError(AppNotInManifestError{Name: "not-here"}))
   578  				Expect(rawAppManifest).To(BeNil())
   579  			})
   580  		})
   581  
   582  	})
   583  })