diff --git a/out/cloud_foundry.go b/out/cloud_foundry.go index 404c16e..1e5ce98 100644 --- a/out/cloud_foundry.go +++ b/out/cloud_foundry.go @@ -5,10 +5,11 @@ import ( "os/exec" ) +//go:generate counterfeiter . PAAS type PAAS interface { Login(api string, username string, password string, insecure bool) error Target(organization string, space string) error - PushApp(manifest string, path string, currentAppName string) error + PushApp(manifest string, path string, currentAppName string, dockerUser string) error } type CloudFoundry struct{} @@ -35,7 +36,7 @@ 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) error { +func (cf *CloudFoundry) PushApp(manifest string, path string, currentAppName string, dockerUser string) error { args := []string{} if currentAppName == "" { @@ -44,6 +45,10 @@ func (cf *CloudFoundry) PushApp(manifest string, path string, currentAppName str args = append(args, "zero-downtime-push", currentAppName, "-f", manifest) } + if dockerUser != "" { + args = append(args, "--docker-username", dockerUser) + } + if path != "" { stat, err := os.Stat(path) if err != nil { diff --git a/out/command.go b/out/command.go index 7e85005..b561583 100644 --- a/out/command.go +++ b/out/command.go @@ -50,6 +50,7 @@ func (command *Command) Run(request Request) (Response, error) { request.Params.ManifestPath, request.Params.Path, request.Params.CurrentAppName, + request.Params.DockerUsername, ) if err != nil { return Response{}, err diff --git a/out/command_test.go b/out/command_test.go index 05ead3e..eaa2bc8 100644 --- a/out/command_test.go +++ b/out/command_test.go @@ -10,20 +10,20 @@ import ( "github.com/concourse/cf-resource" "github.com/concourse/cf-resource/out" - "github.com/concourse/cf-resource/out/fakes" + "github.com/concourse/cf-resource/out/outfakes" "io" "io/ioutil" ) var _ = Describe("Out Command", func() { var ( - cloudFoundry *fakes.FakePAAS + cloudFoundry *outfakes.FakePAAS request out.Request command *out.Command ) BeforeEach(func() { - cloudFoundry = &fakes.FakePAAS{} + cloudFoundry = &outfakes.FakePAAS{} command = out.NewCommand(cloudFoundry) request = out.Request{ @@ -78,10 +78,11 @@ var _ = Describe("Out Command", func() { By("pushing the app") Expect(cloudFoundry.PushAppCallCount()).To(Equal(1)) - manifest, path, currentAppName := cloudFoundry.PushAppArgsForCall(0) + manifest, path, currentAppName, dockerUser := cloudFoundry.PushAppArgsForCall(0) Expect(manifest).To(Equal("assets/manifest.yml")) Expect(path).To(Equal("")) Expect(currentAppName).To(Equal("")) + Expect(dockerUser).To(Equal("")) }) Describe("handling any errors", func() { @@ -219,10 +220,36 @@ 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")) }) + It("lets people define a user for connecting to a docker registry", func() { + request = out.Request{ + Source: resource.Source{ + API: "https://api.run.pivotal.io", + Username: "awesome@example.com", + Password: "hunter2", + Organization: "secret", + Space: "volcano-base", + }, + Params: out.Params{ + ManifestPath: "a/path/to/a/manifest.yml", + CurrentAppName: "cool-app-name", + DockerUsername: "DOCKER_USER", + }, + } + + _, err := command.Run(request) + Expect(err).NotTo(HaveOccurred()) + + By("pushing the app") + Expect(cloudFoundry.PushAppCallCount()).To(Equal(1)) + + _, _, _, dockerUser := cloudFoundry.PushAppArgsForCall(0) + Expect(dockerUser).To(Equal("DOCKER_USER")) + }) + Context("using a docker registry which requires authentication", func() { var savedVar string diff --git a/out/integration_test.go b/out/integration_test.go index f21eb45..c82f541 100644 --- a/out/integration_test.go +++ b/out/integration_test.go @@ -248,4 +248,38 @@ var _ = Describe("Out", func() { }) }) }) + + Context("when accessing a protected docker registry", func() { + BeforeEach(func() { + request.Params.DockerUsername = "DOCKER_USERNAME" + request.Params.DockerPassword = "DOCKER_PASSWORD" + }) + + 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 --docker-username %s", + filepath.Join(tmpDir, "project/manifest.yml"), + request.Params.DockerUsername, + )) + Expect(session.Err).To(gbytes.Say("CF_DOCKER_PASSWORD=DOCKER_PASSWORD")) + }) + }) }) diff --git a/out/models.go b/out/models.go index cac8c94..604b7bd 100644 --- a/out/models.go +++ b/out/models.go @@ -12,6 +12,7 @@ type Params struct { 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"` } diff --git a/out/fakes/fake_paas.go b/out/outfakes/fake_paas.go similarity index 52% rename from out/fakes/fake_paas.go rename to out/outfakes/fake_paas.go index 8cc7d71..ec7365a 100644 --- a/out/fakes/fake_paas.go +++ b/out/outfakes/fake_paas.go @@ -1,9 +1,10 @@ -// This file was generated by counterfeiter -package fakes +// Code generated by counterfeiter. DO NOT EDIT. +package outfakes import ( - "github.com/concourse/cf-resource/out" "sync" + + "github.com/concourse/cf-resource/out" ) type FakePAAS struct { @@ -18,6 +19,9 @@ type FakePAAS struct { loginReturns struct { result1 error } + loginReturnsOnCall map[int]struct { + result1 error + } TargetStub func(organization string, space string) error targetMutex sync.RWMutex targetArgsForCall []struct { @@ -27,32 +31,45 @@ type FakePAAS struct { targetReturns struct { result1 error } - PushAppStub func(manifest string, path string, currentAppName string) error + targetReturnsOnCall map[int]struct { + result1 error + } + PushAppStub func(manifest string, path string, currentAppName string, dockerUser string) error pushAppMutex sync.RWMutex pushAppArgsForCall []struct { manifest string path string currentAppName string + dockerUser string } pushAppReturns struct { result1 error } + pushAppReturnsOnCall map[int]struct { + result1 error + } + invocations map[string][][]interface{} + invocationsMutex sync.RWMutex } func (fake *FakePAAS) Login(api string, username string, password string, insecure bool) error { fake.loginMutex.Lock() + ret, specificReturn := fake.loginReturnsOnCall[len(fake.loginArgsForCall)] fake.loginArgsForCall = append(fake.loginArgsForCall, struct { api string username string password string insecure bool }{api, username, password, insecure}) + fake.recordInvocation("Login", []interface{}{api, username, password, insecure}) fake.loginMutex.Unlock() if fake.LoginStub != nil { return fake.LoginStub(api, username, password, insecure) - } else { - return fake.loginReturns.result1 } + if specificReturn { + return ret.result1 + } + return fake.loginReturns.result1 } func (fake *FakePAAS) LoginCallCount() int { @@ -74,18 +91,34 @@ func (fake *FakePAAS) LoginReturns(result1 error) { }{result1} } +func (fake *FakePAAS) LoginReturnsOnCall(i int, result1 error) { + fake.LoginStub = nil + if fake.loginReturnsOnCall == nil { + fake.loginReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.loginReturnsOnCall[i] = struct { + result1 error + }{result1} +} + func (fake *FakePAAS) Target(organization string, space string) error { fake.targetMutex.Lock() + ret, specificReturn := fake.targetReturnsOnCall[len(fake.targetArgsForCall)] fake.targetArgsForCall = append(fake.targetArgsForCall, struct { organization string space string }{organization, space}) + fake.recordInvocation("Target", []interface{}{organization, space}) fake.targetMutex.Unlock() if fake.TargetStub != nil { return fake.TargetStub(organization, space) - } else { - return fake.targetReturns.result1 } + if specificReturn { + return ret.result1 + } + return fake.targetReturns.result1 } func (fake *FakePAAS) TargetCallCount() int { @@ -107,19 +140,36 @@ func (fake *FakePAAS) TargetReturns(result1 error) { }{result1} } -func (fake *FakePAAS) PushApp(manifest string, path string, currentAppName string) error { +func (fake *FakePAAS) TargetReturnsOnCall(i int, result1 error) { + fake.TargetStub = nil + if fake.targetReturnsOnCall == nil { + fake.targetReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.targetReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakePAAS) PushApp(manifest string, path string, currentAppName string, dockerUser string) error { fake.pushAppMutex.Lock() + ret, specificReturn := fake.pushAppReturnsOnCall[len(fake.pushAppArgsForCall)] fake.pushAppArgsForCall = append(fake.pushAppArgsForCall, struct { manifest string path string currentAppName string - }{manifest, path, currentAppName}) + dockerUser string + }{manifest, path, currentAppName, dockerUser}) + fake.recordInvocation("PushApp", []interface{}{manifest, path, currentAppName, dockerUser}) fake.pushAppMutex.Unlock() if fake.PushAppStub != nil { - return fake.PushAppStub(manifest, path, currentAppName) - } else { - return fake.pushAppReturns.result1 + return fake.PushAppStub(manifest, path, currentAppName, dockerUser) + } + if specificReturn { + return ret.result1 } + return fake.pushAppReturns.result1 } func (fake *FakePAAS) PushAppCallCount() int { @@ -128,10 +178,10 @@ func (fake *FakePAAS) PushAppCallCount() int { return len(fake.pushAppArgsForCall) } -func (fake *FakePAAS) PushAppArgsForCall(i int) (string, string, string) { +func (fake *FakePAAS) PushAppArgsForCall(i int) (string, string, string, string) { fake.pushAppMutex.RLock() defer fake.pushAppMutex.RUnlock() - return fake.pushAppArgsForCall[i].manifest, fake.pushAppArgsForCall[i].path, fake.pushAppArgsForCall[i].currentAppName + return fake.pushAppArgsForCall[i].manifest, fake.pushAppArgsForCall[i].path, fake.pushAppArgsForCall[i].currentAppName, fake.pushAppArgsForCall[i].dockerUser } func (fake *FakePAAS) PushAppReturns(result1 error) { @@ -141,4 +191,44 @@ func (fake *FakePAAS) PushAppReturns(result1 error) { }{result1} } +func (fake *FakePAAS) PushAppReturnsOnCall(i int, result1 error) { + fake.PushAppStub = nil + if fake.pushAppReturnsOnCall == nil { + fake.pushAppReturnsOnCall = make(map[int]struct { + result1 error + }) + } + fake.pushAppReturnsOnCall[i] = struct { + result1 error + }{result1} +} + +func (fake *FakePAAS) Invocations() map[string][][]interface{} { + fake.invocationsMutex.RLock() + defer fake.invocationsMutex.RUnlock() + fake.loginMutex.RLock() + defer fake.loginMutex.RUnlock() + fake.targetMutex.RLock() + defer fake.targetMutex.RUnlock() + fake.pushAppMutex.RLock() + defer fake.pushAppMutex.RUnlock() + copiedInvocations := map[string][][]interface{}{} + for key, value := range fake.invocations { + copiedInvocations[key] = value + } + return copiedInvocations +} + +func (fake *FakePAAS) recordInvocation(key string, args []interface{}) { + fake.invocationsMutex.Lock() + defer fake.invocationsMutex.Unlock() + if fake.invocations == nil { + fake.invocations = map[string][][]interface{}{} + } + if fake.invocations[key] == nil { + fake.invocations[key] = [][]interface{}{} + } + fake.invocations[key] = append(fake.invocations[key], args) +} + var _ out.PAAS = new(FakePAAS)