github.com/cloudfoundry-community/cloudfoundry-cli@v6.44.1-0.20240130060226-cda5ed8e89a5+incompatible/util/manifestparser/parser_test.go (about)

     1  package manifestparser_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	. "code.cloudfoundry.org/cli/util/manifestparser"
    11  
    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  
   176  			executeErr error
   177  
   178  			rawManifest []byte
   179  		)
   180  
   181  		BeforeEach(func() {
   182  			tempFile, err := ioutil.TempFile("", "manifest-test-")
   183  			Expect(err).ToNot(HaveOccurred())
   184  			Expect(tempFile.Close()).ToNot(HaveOccurred())
   185  			pathToManifest = tempFile.Name()
   186  			vars = nil
   187  
   188  			pathsToVarsFiles = nil
   189  		})
   190  
   191  		AfterEach(func() {
   192  			Expect(os.RemoveAll(pathToManifest)).ToNot(HaveOccurred())
   193  			for _, path := range pathsToVarsFiles {
   194  				Expect(os.RemoveAll(path)).ToNot(HaveOccurred())
   195  			}
   196  		})
   197  
   198  		JustBeforeEach(func() {
   199  			executeErr = parser.InterpolateAndParse(pathToManifest, pathsToVarsFiles, vars)
   200  		})
   201  
   202  		Context("regardless of whether the manifest needs interpolation", func() {
   203  			BeforeEach(func() {
   204  				rawManifest = []byte(`---
   205  applications:
   206  - name: spark
   207    memory: 1G
   208    instances: 2
   209  - name: flame
   210    memory: 1G
   211    instances: 2
   212  `)
   213  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   214  				Expect(err).ToNot(HaveOccurred())
   215  			})
   216  
   217  			It("parses the manifest properly", func() {
   218  				Expect(executeErr).ToNot(HaveOccurred())
   219  
   220  				Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   221  				Expect(parser.PathToManifest).To(Equal(pathToManifest))
   222  				Expect(parser.FullRawManifest()).To(MatchYAML(rawManifest))
   223  			})
   224  		})
   225  
   226  		Context("invalid yaml is passed", func() {
   227  			BeforeEach(func() {
   228  				rawManifest = []byte("\t\t")
   229  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   230  				Expect(err).ToNot(HaveOccurred())
   231  			})
   232  
   233  			It("parses the manifest properly", func() {
   234  				Expect(executeErr).To(HaveOccurred())
   235  			})
   236  		})
   237  
   238  		When("the manifest contains variables that need interpolation", func() {
   239  			BeforeEach(func() {
   240  				rawManifest = []byte(`---
   241  applications:
   242  - name: ((var1))
   243  - name: ((var2))
   244  `)
   245  				err := ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   246  				Expect(err).ToNot(HaveOccurred())
   247  			})
   248  
   249  			When("only vars files are provided", func() {
   250  				var (
   251  					varsDir string
   252  				)
   253  
   254  				BeforeEach(func() {
   255  					var err error
   256  					varsDir, err = ioutil.TempDir("", "vars-test")
   257  					Expect(err).ToNot(HaveOccurred())
   258  
   259  					varsFilePath1 := filepath.Join(varsDir, "vars-1")
   260  					err = ioutil.WriteFile(varsFilePath1, []byte("var1: spark"), 0666)
   261  					Expect(err).ToNot(HaveOccurred())
   262  
   263  					varsFilePath2 := filepath.Join(varsDir, "vars-2")
   264  					err = ioutil.WriteFile(varsFilePath2, []byte("var2: flame"), 0666)
   265  					Expect(err).ToNot(HaveOccurred())
   266  
   267  					pathsToVarsFiles = append(pathsToVarsFiles, varsFilePath1, varsFilePath2)
   268  				})
   269  
   270  				AfterEach(func() {
   271  					Expect(os.RemoveAll(varsDir)).ToNot(HaveOccurred())
   272  				})
   273  
   274  				When("multiple values for the same variable(s) are provided", func() {
   275  					BeforeEach(func() {
   276  						varsFilePath1 := filepath.Join(varsDir, "vars-1")
   277  						err := ioutil.WriteFile(varsFilePath1, []byte("var1: garbageapp\nvar1: spark\nvar2: doesn't matter"), 0666)
   278  						Expect(err).ToNot(HaveOccurred())
   279  
   280  						varsFilePath2 := filepath.Join(varsDir, "vars-2")
   281  						err = ioutil.WriteFile(varsFilePath2, []byte("var2: flame"), 0666)
   282  						Expect(err).ToNot(HaveOccurred())
   283  
   284  						pathsToVarsFiles = append(pathsToVarsFiles, varsFilePath1, varsFilePath2)
   285  					})
   286  
   287  					It("interpolates the placeholder values", func() {
   288  						Expect(executeErr).ToNot(HaveOccurred())
   289  						Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   290  					})
   291  				})
   292  
   293  				When("the provided files exists and contain valid yaml", func() {
   294  					It("interpolates the placeholder values", func() {
   295  						Expect(executeErr).ToNot(HaveOccurred())
   296  						Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   297  					})
   298  				})
   299  
   300  				When("a variable in the manifest is not provided in the vars file", func() {
   301  					BeforeEach(func() {
   302  						varsFilePath := filepath.Join(varsDir, "vars-1")
   303  						err := ioutil.WriteFile(varsFilePath, []byte("notvar: foo"), 0666)
   304  						Expect(err).ToNot(HaveOccurred())
   305  
   306  						pathsToVarsFiles = []string{varsFilePath}
   307  					})
   308  
   309  					It("returns an error", func() {
   310  						Expect(executeErr.Error()).To(Equal("Expected to find variables: var1, var2"))
   311  					})
   312  				})
   313  
   314  				When("the provided file path does not exist", func() {
   315  					BeforeEach(func() {
   316  						pathsToVarsFiles = []string{"garbagepath"}
   317  					})
   318  
   319  					It("returns an error", func() {
   320  						Expect(executeErr).To(HaveOccurred())
   321  						Expect(os.IsNotExist(executeErr)).To(BeTrue())
   322  					})
   323  				})
   324  
   325  				When("the provided file is not a valid yaml file", func() {
   326  					BeforeEach(func() {
   327  						varsFilePath := filepath.Join(varsDir, "vars-1")
   328  						err := ioutil.WriteFile(varsFilePath, []byte(": bad"), 0666)
   329  						Expect(err).ToNot(HaveOccurred())
   330  
   331  						pathsToVarsFiles = []string{varsFilePath}
   332  					})
   333  
   334  					It("returns an error", func() {
   335  						Expect(executeErr).To(HaveOccurred())
   336  						Expect(executeErr).To(MatchError(InvalidYAMLError{
   337  							Err: errors.New("yaml: did not find expected key"),
   338  						}))
   339  					})
   340  				})
   341  			})
   342  
   343  			When("only vars are provided", func() {
   344  				BeforeEach(func() {
   345  					vars = []template.VarKV{
   346  						{Name: "var1", Value: "spark"},
   347  						{Name: "var2", Value: "flame"},
   348  					}
   349  				})
   350  
   351  				It("interpolates the placeholder values", func() {
   352  					Expect(executeErr).ToNot(HaveOccurred())
   353  					Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   354  				})
   355  			})
   356  
   357  			When("vars and vars files are provided", func() {
   358  				var varsFilePath string
   359  				BeforeEach(func() {
   360  					tmp, err := ioutil.TempFile("", "util-manifest-varsilfe")
   361  					Expect(err).NotTo(HaveOccurred())
   362  					Expect(tmp.Close()).NotTo(HaveOccurred())
   363  
   364  					varsFilePath = tmp.Name()
   365  					err = ioutil.WriteFile(varsFilePath, []byte("var1: spark\nvar2: 12345"), 0666)
   366  					Expect(err).ToNot(HaveOccurred())
   367  
   368  					pathsToVarsFiles = []string{varsFilePath}
   369  					vars = []template.VarKV{
   370  						{Name: "var2", Value: "flame"},
   371  					}
   372  				})
   373  
   374  				AfterEach(func() {
   375  					Expect(os.RemoveAll(varsFilePath)).ToNot(HaveOccurred())
   376  				})
   377  
   378  				It("interpolates the placeholder values, prioritizing the vars flag", func() {
   379  					Expect(executeErr).ToNot(HaveOccurred())
   380  					Expect(parser.AppNames()).To(ConsistOf("spark", "flame"))
   381  				})
   382  			})
   383  		})
   384  	})
   385  
   386  	Describe("RawAppManifest", func() {
   387  		var (
   388  			rawAppManifest []byte
   389  			appName        string
   390  			executeErr     error
   391  			rawManifest    []byte
   392  			pathToManifest string
   393  			tmpMyPath      string
   394  		)
   395  
   396  		BeforeEach(func() {
   397  			var err error
   398  
   399  			appName = "spark"
   400  
   401  			tmpMyPath, err = ioutil.TempDir("", "")
   402  			Expect(err).ToNot(HaveOccurred())
   403  
   404  			rawManifest = []byte(fmt.Sprintf(`---
   405  applications:
   406  - name: spark
   407    memory: 1G
   408    instances: 2
   409    docker:
   410      username: experiment
   411    path: %s
   412  - name: flame
   413    memory: 1G
   414    instances: 2
   415    docker:
   416      username: experiment
   417  `, tmpMyPath))
   418  
   419  		})
   420  
   421  		JustBeforeEach(func() {
   422  			tempFile, err := ioutil.TempFile("", "manifest-test-")
   423  			Expect(err).ToNot(HaveOccurred())
   424  			Expect(tempFile.Close()).ToNot(HaveOccurred())
   425  			pathToManifest = tempFile.Name()
   426  			err = ioutil.WriteFile(pathToManifest, rawManifest, 0666)
   427  			Expect(err).ToNot(HaveOccurred())
   428  			err = parser.InterpolateAndParse(pathToManifest, nil, nil)
   429  			Expect(err).ToNot(HaveOccurred())
   430  			rawAppManifest, executeErr = parser.RawAppManifest(appName)
   431  		})
   432  
   433  		AfterEach(func() {
   434  			err := os.RemoveAll(pathToManifest)
   435  			Expect(err).ToNot(HaveOccurred())
   436  		})
   437  
   438  		When("marshaling does not error", func() {
   439  
   440  			It("returns just the app's manifest", func() {
   441  				Expect(executeErr).ToNot(HaveOccurred())
   442  				Expect(string(rawAppManifest)).To(MatchYAML(fmt.Sprintf(`applications:
   443  - name: spark
   444    memory: 1G
   445    instances: 2
   446    docker:
   447      username: experiment
   448    path: %s`, tmpMyPath)))
   449  			})
   450  		})
   451  
   452  		When("The app is not present", func() {
   453  			BeforeEach(func() {
   454  				appName = "not-here"
   455  			})
   456  
   457  			It("returns an error", func() {
   458  				Expect(executeErr).To(MatchError(AppNotInManifestError{Name: "not-here"}))
   459  				Expect(rawAppManifest).To(BeNil())
   460  			})
   461  		})
   462  
   463  	})
   464  })