diff --git a/README.md b/README.md index c6a4340..6e56b2f 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,8 @@ be specified. that this will re-deploy over. If this is set the resource will perform a zero-downtime deploy. * `environment_variables`: *Optional.* It is not necessary to set the variables in [manifest][cf-manifests] if this parameter is set. +* `vars`: *Optional.* Map of variables to pass to manifest +* `vars_files`: *Optional.* List of variables files to pass to manifest * `docker_username`: *Optional.* This is used as the username to authenticate against a protected docker registry. * `docker_password`: *Optional.* This should be the users password when authenticating against a protected docker registry. * `show_app_log`: *Optional.* Tails the app log during startup, useful to debug issues when using blue/green deploys together with the `current_app_name` option. diff --git a/out/cloud_foundry.go b/out/cloud_foundry.go index 63a3439..fc9d045 100644 --- a/out/cloud_foundry.go +++ b/out/cloud_foundry.go @@ -1,6 +1,7 @@ package out import ( + "fmt" "os" "os/exec" ) @@ -9,7 +10,7 @@ import ( type PAAS interface { Login(api string, username string, password string, clientID string, clientSecret string, insecure bool) error Target(organization string, space string) error - PushApp(manifest string, path string, currentAppName string, dockerUser string, showLogs bool, noStart bool) error + PushApp(manifest string, path string, currentAppName string, vars map[string]interface{}, varsFiles []string, dockerUser string, showLogs bool, noStart bool) error } type CloudFoundry struct { @@ -41,7 +42,15 @@ func (cf *CloudFoundry) Target(organization string, space string) error { return cf.cf("target", "-o", organization, "-s", space).Run() } -func (cf *CloudFoundry) PushApp(manifest string, path string, currentAppName string, dockerUser string, showLogs bool, noStart bool) error { +func (cf *CloudFoundry) PushApp( + manifest string, + path string, currentAppName string, + vars map[string]interface{}, + varsFiles []string, + dockerUser string, + showLogs bool, + noStart bool, +) error { args := []string{} if currentAppName == "" { @@ -56,6 +65,14 @@ func (cf *CloudFoundry) PushApp(manifest string, path string, currentAppName str } } + for name, value := range vars { + args = append(args, "--var", fmt.Sprintf("%s=%s", name, value)) + } + + for _, varsFile := range varsFiles { + args = append(args, "--vars-file", varsFile) + } + if dockerUser != "" { args = append(args, "--docker-username", dockerUser) } diff --git a/out/command.go b/out/command.go index 8364d6a..737197f 100644 --- a/out/command.go +++ b/out/command.go @@ -53,6 +53,8 @@ func (command *Command) Run(request Request) (Response, error) { request.Params.ManifestPath, request.Params.Path, request.Params.CurrentAppName, + request.Params.Vars, + request.Params.VarsFiles, request.Params.DockerUsername, request.Params.ShowAppLog, request.Params.NoStart, diff --git a/out/command_test.go b/out/command_test.go index d3b6e85..581719e 100644 --- a/out/command_test.go +++ b/out/command_test.go @@ -37,6 +37,8 @@ var _ = Describe("Out Command", func() { }, Params: out.Params{ ManifestPath: "assets/manifest.yml", + Vars: map[string]interface{}{"foo": "bar"}, + VarsFiles: []string{"vars.yml"}, }, } }) @@ -81,10 +83,12 @@ var _ = Describe("Out Command", func() { By("pushing the app") Expect(cloudFoundry.PushAppCallCount()).To(Equal(1)) - manifest, path, currentAppName, dockerUser, showAppLog, noStart := cloudFoundry.PushAppArgsForCall(0) + manifest, path, currentAppName, vars, varsFiles, dockerUser, showAppLog, noStart := cloudFoundry.PushAppArgsForCall(0) Expect(manifest).To(Equal(request.Params.ManifestPath)) Expect(path).To(Equal("")) Expect(currentAppName).To(Equal("")) + Expect(vars).To(Equal(map[string]interface{}{"foo": "bar"})) + Expect(varsFiles).To(Equal([]string{"vars.yml"})) Expect(dockerUser).To(Equal("")) Expect(showAppLog).To(Equal(false)) Expect(noStart).To(Equal(false)) @@ -129,7 +133,7 @@ var _ = Describe("Out Command", func() { _, err := command.Run(request) Expect(err).NotTo(HaveOccurred()) - _, _, _, _, _, noStart := cloudFoundry.PushAppArgsForCall(0) + _, _, _, _, _, _, _, noStart := cloudFoundry.PushAppArgsForCall(0) Expect(noStart).To(Equal(true)) }) }) @@ -272,7 +276,7 @@ var _ = Describe("Out Command", func() { By("pushing the app") Expect(cloudFoundry.PushAppCallCount()).To(Equal(1)) - _, _, currentAppName, _, _, _ := cloudFoundry.PushAppArgsForCall(0) + _, _, currentAppName, _, _, _, _, _ := cloudFoundry.PushAppArgsForCall(0) Expect(currentAppName).To(Equal("cool-app-name")) }) @@ -298,7 +302,7 @@ var _ = Describe("Out Command", func() { By("pushing the app") Expect(cloudFoundry.PushAppCallCount()).To(Equal(1)) - _, _, _, dockerUser, _, _ := cloudFoundry.PushAppArgsForCall(0) + _, _, _, _, _, dockerUser, _, _ := cloudFoundry.PushAppArgsForCall(0) Expect(dockerUser).To(Equal("DOCKER_USER")) }) diff --git a/out/integration_test.go b/out/integration_test.go index 56b9e99..7101654 100644 --- a/out/integration_test.go +++ b/out/integration_test.go @@ -123,6 +123,43 @@ var _ = Describe("Out", func() { }) }) + Context("when specifying vars", func() { + BeforeEach(func() { + request.Params.Vars = map[string]interface{}{"foo": "bar"} + request.Params.VarsFiles = []string{"vars.yml"} + }) + + It("pushes an application to cloud foundry", func() { + session, err := gexec.Start( + cmd, + GinkgoWriter, + GinkgoWriter, + ) + Expect(err).NotTo(HaveOccurred()) + + Eventually(session).Should(gexec.Exit(0)) + + var response out.Response + err = json.Unmarshal(session.Out.Contents(), &response) + Expect(err).NotTo(HaveOccurred()) + + Expect(response.Version.Timestamp).To(BeTemporally("~", time.Now(), time.Second)) + + // shim outputs arguments + Expect(session.Err).To(gbytes.Say("cf api https://api.run.pivotal.io --skip-ssl-validation")) + Expect(session.Err).To(gbytes.Say("cf auth awesome@example.com hunter2")) + Expect(session.Err).To(gbytes.Say("cf target -o org -s space")) + Expect(session.Err).To(gbytes.Say("cf zero-downtime-push awesome-app -f %s --var foo=bar --vars-file vars.yml", + filepath.Join(tmpDir, "project/manifest.yml"), + )) + Expect(session.Err).To(gbytes.Say(filepath.Join(tmpDir, "another-project"))) + + // color should be always + Eventually(session.Err).Should(gbytes.Say("CF_COLOR=true")) + Eventually(session.Err).Should(gbytes.Say("CF_TRACE=/dev/stderr")) + }) + }) + Context("when my manifest and file paths contain a glob", func() { var tmpFileManifest *os.File var tmpFileSearch *os.File diff --git a/out/models.go b/out/models.go index 80464a8..aa09655 100644 --- a/out/models.go +++ b/out/models.go @@ -8,14 +8,16 @@ type Request struct { } type Params struct { - ManifestPath string `json:"manifest"` - Path string `json:"path"` - CurrentAppName string `json:"current_app_name"` - EnvironmentVariables map[string]string `json:"environment_variables"` - DockerUsername string `json:"docker_username"` - DockerPassword string `json:"docker_password"` - ShowAppLog bool `json:"show_app_log"` - NoStart bool `json:"no_start"` + ManifestPath string `json:"manifest"` + Path string `json:"path"` + CurrentAppName string `json:"current_app_name"` + Vars map[string]interface{} `json:"vars"` + VarsFiles []string `json:"vars_files"` + EnvironmentVariables map[string]string `json:"environment_variables"` + DockerUsername string `json:"docker_username"` + DockerPassword string `json:"docker_password"` + ShowAppLog bool `json:"show_app_log"` + NoStart bool `json:"no_start"` } type Response struct { diff --git a/out/outfakes/fake_paas.go b/out/outfakes/fake_paas.go index 8a5d3a8..3e9e17e 100644 --- a/out/outfakes/fake_paas.go +++ b/out/outfakes/fake_paas.go @@ -36,12 +36,14 @@ type FakePAAS struct { targetReturnsOnCall map[int]struct { result1 error } - PushAppStub func(manifest string, path string, currentAppName string, dockerUser string, showLogs bool, noStart bool) error + PushAppStub func(manifest string, path string, currentAppName string, vars map[string]interface{}, varsFiles []string, dockerUser string, showLogs bool, noStart bool) error pushAppMutex sync.RWMutex pushAppArgsForCall []struct { manifest string path string currentAppName string + vars map[string]interface{} + varsFiles []string dockerUser string showLogs bool noStart bool @@ -158,21 +160,28 @@ func (fake *FakePAAS) TargetReturnsOnCall(i int, result1 error) { }{result1} } -func (fake *FakePAAS) PushApp(manifest string, path string, currentAppName string, dockerUser string, showLogs bool, noStart bool) error { +func (fake *FakePAAS) PushApp(manifest string, path string, currentAppName string, vars map[string]interface{}, varsFiles []string, dockerUser string, showLogs bool, noStart bool) error { + var varsFilesCopy []string + if varsFiles != nil { + varsFilesCopy = make([]string, len(varsFiles)) + copy(varsFilesCopy, varsFiles) + } fake.pushAppMutex.Lock() ret, specificReturn := fake.pushAppReturnsOnCall[len(fake.pushAppArgsForCall)] fake.pushAppArgsForCall = append(fake.pushAppArgsForCall, struct { manifest string path string currentAppName string + vars map[string]interface{} + varsFiles []string dockerUser string showLogs bool noStart bool - }{manifest, path, currentAppName, dockerUser, showLogs, noStart}) - fake.recordInvocation("PushApp", []interface{}{manifest, path, currentAppName, dockerUser, showLogs, noStart}) + }{manifest, path, currentAppName, vars, varsFilesCopy, dockerUser, showLogs, noStart}) + fake.recordInvocation("PushApp", []interface{}{manifest, path, currentAppName, vars, varsFilesCopy, dockerUser, showLogs, noStart}) fake.pushAppMutex.Unlock() if fake.PushAppStub != nil { - return fake.PushAppStub(manifest, path, currentAppName, dockerUser, showLogs, noStart) + return fake.PushAppStub(manifest, path, currentAppName, vars, varsFiles, dockerUser, showLogs, noStart) } if specificReturn { return ret.result1 @@ -186,10 +195,10 @@ func (fake *FakePAAS) PushAppCallCount() int { return len(fake.pushAppArgsForCall) } -func (fake *FakePAAS) PushAppArgsForCall(i int) (string, string, string, string, bool, bool) { +func (fake *FakePAAS) PushAppArgsForCall(i int) (string, string, string, map[string]interface{}, []string, string, bool, bool) { fake.pushAppMutex.RLock() defer fake.pushAppMutex.RUnlock() - return fake.pushAppArgsForCall[i].manifest, fake.pushAppArgsForCall[i].path, fake.pushAppArgsForCall[i].currentAppName, fake.pushAppArgsForCall[i].dockerUser, fake.pushAppArgsForCall[i].showLogs, fake.pushAppArgsForCall[i].noStart + return fake.pushAppArgsForCall[i].manifest, fake.pushAppArgsForCall[i].path, fake.pushAppArgsForCall[i].currentAppName, fake.pushAppArgsForCall[i].vars, fake.pushAppArgsForCall[i].varsFiles, fake.pushAppArgsForCall[i].dockerUser, fake.pushAppArgsForCall[i].showLogs, fake.pushAppArgsForCall[i].noStart } func (fake *FakePAAS) PushAppReturns(result1 error) {