Skip to content

Commit 8fb856f

Browse files
ewrenn8XenoPhex
authored andcommittedNov 21, 2018
add docker support to v7 push
- Will prompt for password when docker username is provided. - Added some additional logging to Conceptualize. - Added create app event to align with updating app events. - Added close to ReadArchive as the archive should be closed on this error. If the consumer does not know to close the returned archive upon the stat error, the CLI could have a hanging file descriptor. - Refactored out CreateDockerPackageByApplication from CreateDockerPackageByApplicationNameAndSpace in order to reduce API calls in push. - Refactored Actualize into smaller pieces to make it easier to read. [Finishes #161764452] Signed-off-by: Anand Gaitonde <agaitonde@pivotal.io>
1 parent aada986 commit 8fb856f

File tree

12 files changed

+436
-134
lines changed

12 files changed

+436
-134
lines changed
 

‎actor/sharedaction/resource.go

+1
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,7 @@ func (Actor) ReadArchive(archivePath string) (io.ReadCloser, int64, error) {
472472

473473
archiveInfo, err := archive.Stat()
474474
if err != nil {
475+
archive.Close()
475476
log.WithField("archivePath", archivePath).Errorln("stat temp archive:", err)
476477
return nil, -1, err
477478
}

‎actor/v7action/package.go

+10-9
Original file line numberDiff line numberDiff line change
@@ -25,26 +25,27 @@ type DockerImageCredentials struct {
2525
Password string
2626
}
2727

28-
func (actor Actor) CreateDockerPackageByApplicationNameAndSpace(appName string, spaceGUID string, dockerImageCredentials DockerImageCredentials) (Package, Warnings, error) {
29-
app, allWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID)
30-
if err != nil {
31-
return Package{}, allWarnings, err
32-
}
28+
func (actor Actor) CreateDockerPackageByApplication(appGUID string, dockerImageCredentials DockerImageCredentials) (Package, Warnings, error) {
3329
inputPackage := ccv3.Package{
3430
Type: constant.PackageTypeDocker,
3531
Relationships: ccv3.Relationships{
36-
constant.RelationshipTypeApplication: ccv3.Relationship{GUID: app.GUID},
32+
constant.RelationshipTypeApplication: ccv3.Relationship{GUID: appGUID},
3733
},
3834
DockerImage: dockerImageCredentials.Path,
3935
DockerUsername: dockerImageCredentials.Username,
4036
DockerPassword: dockerImageCredentials.Password,
4137
}
4238
pkg, warnings, err := actor.CloudControllerClient.CreatePackage(inputPackage)
43-
allWarnings = append(allWarnings, warnings...)
39+
return Package(pkg), Warnings(warnings), err
40+
}
41+
42+
func (actor Actor) CreateDockerPackageByApplicationNameAndSpace(appName string, spaceGUID string, dockerImageCredentials DockerImageCredentials) (Package, Warnings, error) {
43+
app, getWarnings, err := actor.GetApplicationByNameAndSpace(appName, spaceGUID)
4444
if err != nil {
45-
return Package{}, allWarnings, err
45+
return Package{}, getWarnings, err
4646
}
47-
return Package(pkg), allWarnings, err
47+
pkg, warnings, err := actor.CreateDockerPackageByApplication(app.GUID, dockerImageCredentials)
48+
return pkg, append(getWarnings, warnings...), err
4849
}
4950

5051
func (actor Actor) CreateAndUploadBitsPackageByApplicationNameAndSpace(appName string, spaceGUID string, bitsPath string) (Package, Warnings, error) {

‎actor/v7pushaction/actualize.go

+161-102
Original file line numberDiff line numberDiff line change
@@ -28,129 +28,42 @@ func (actor Actor) Actualize(state PushState, progressBar ProgressBar) (
2828
defer close(warningsStream)
2929
defer close(errorStream)
3030

31-
if state.Application.GUID != "" {
32-
log.WithField("Name", state.Application.Name).Info("skipping app creation as it has a GUID")
33-
eventStream <- SkippingApplicationCreation
34-
application, warnings, err := actor.V7Actor.UpdateApplication(state.Application)
35-
state.Application = application
36-
warningsStream <- Warnings(warnings)
37-
if err != nil {
38-
errorStream <- err
39-
return
40-
}
41-
eventStream <- UpdatedApplication
42-
43-
} else {
44-
log.WithField("Name", state.Application.Name).Info("creating app")
45-
createdApp, createWarnings, err := actor.V7Actor.CreateApplicationInSpace(state.Application, state.SpaceGUID)
46-
warningsStream <- Warnings(createWarnings)
47-
if err != nil {
48-
errorStream <- err
49-
return
50-
}
51-
52-
state.Application = createdApp
53-
eventStream <- CreatedApplication
54-
}
55-
stateStream <- state
31+
var err error
5632

57-
if state.Overrides.Memory.IsSet {
58-
log.Info("Scaling Web Process")
59-
eventStream <- ScaleWebProcess
60-
process := v7action.Process{Type: constant.ProcessTypeWeb, MemoryInMB: state.Overrides.Memory}
61-
scaleWarnings, err := actor.V7Actor.ScaleProcessByApplication(state.Application.GUID, process)
62-
warningsStream <- Warnings(scaleWarnings)
63-
if err != nil {
64-
errorStream <- err
65-
return
66-
}
67-
eventStream <- ScaleWebProcessComplete
68-
}
69-
70-
if state.Overrides.StartCommand.IsSet || state.Overrides.HealthCheckType != "" {
71-
log.Info("Setting Web Process's Configuration")
72-
eventStream <- SetProcessConfiguration
73-
74-
var process v7action.Process
75-
if state.Overrides.StartCommand.IsSet {
76-
process.Command = state.Overrides.StartCommand
77-
}
78-
if state.Overrides.HealthCheckType != "" {
79-
process.HealthCheckType = state.Overrides.HealthCheckType
80-
process.HealthCheckEndpoint = constant.ProcessHealthCheckEndpointDefault
81-
}
82-
83-
log.WithField("Process", process).Debug("Update process")
84-
warnings, err := actor.V7Actor.UpdateProcessByTypeAndApplication(constant.ProcessTypeWeb, state.Application.GUID, process)
85-
warningsStream <- Warnings(warnings)
86-
if err != nil {
87-
errorStream <- err
88-
return
89-
}
90-
eventStream <- SetProcessConfigurationComplete
91-
}
92-
93-
eventStream <- CreatingAndMappingRoutes
94-
routeWarnings, routeErr := actor.CreateAndMapDefaultApplicationRoute(state.OrgGUID, state.SpaceGUID, state.Application)
95-
warningsStream <- Warnings(routeWarnings)
96-
if routeErr != nil {
97-
errorStream <- routeErr
33+
state, err = actor.CreateOrUpdateApplication(state, warningsStream, eventStream)
34+
if err != nil {
35+
errorStream <- err
9836
return
9937
}
100-
eventStream <- CreatedRoutes
101-
102-
log.WithField("Path", state.BitsPath).Info(string(CreatingArchive))
38+
stateStream <- state
10339

104-
eventStream <- CreatingArchive
105-
archivePath, err := actor.SharedActor.ZipDirectoryResources(state.BitsPath, state.AllResources)
40+
err = actor.ScaleProcess(state, warningsStream, eventStream)
10641
if err != nil {
10742
errorStream <- err
10843
return
10944
}
110-
defer os.RemoveAll(archivePath)
11145

112-
eventStream <- CreatingPackage
113-
log.WithField("GUID", state.Application.GUID).Info("creating package")
114-
pkg, warnings, err := actor.V7Actor.CreateBitsPackageByApplication(state.Application.GUID)
115-
warningsStream <- Warnings(warnings)
46+
err = actor.UpdateProcess(state, warningsStream, eventStream)
11647
if err != nil {
11748
errorStream <- err
11849
return
11950
}
12051

121-
for count := 0; count < PushRetries; count++ {
122-
eventStream <- ReadingArchive
123-
log.WithField("GUID", state.Application.GUID).Info("creating package")
124-
file, size, readErr := actor.SharedActor.ReadArchive(archivePath)
125-
if readErr != nil {
126-
errorStream <- readErr
127-
return
128-
}
129-
defer file.Close()
130-
131-
eventStream <- UploadingApplicationWithArchive
132-
progressReader := progressBar.NewProgressBarWrapper(file, size)
133-
pkg, warnings, err = actor.V7Actor.UploadBitsPackage(pkg, state.MatchedResources, progressReader, size)
134-
warningsStream <- Warnings(warnings)
135-
136-
if _, ok := err.(ccerror.PipeSeekError); ok {
137-
eventStream <- RetryUpload
138-
continue
139-
}
140-
break
52+
eventStream <- CreatingAndMappingRoutes
53+
routeWarnings, routeErr := actor.CreateAndMapDefaultApplicationRoute(state.OrgGUID, state.SpaceGUID, state.Application)
54+
warningsStream <- Warnings(routeWarnings)
55+
if routeErr != nil {
56+
errorStream <- routeErr
57+
return
14158
}
59+
eventStream <- CreatedRoutes
14260

61+
pkg, err := actor.CreatePackage(state, progressBar, warningsStream, eventStream)
14362
if err != nil {
144-
if e, ok := err.(ccerror.PipeSeekError); ok {
145-
errorStream <- actionerror.UploadFailedError{Err: e.Err}
146-
return
147-
}
14863
errorStream <- err
14964
return
15065
}
15166

152-
eventStream <- UploadWithArchiveComplete
153-
15467
polledPackage, warnings, err := actor.V7Actor.PollPackage(pkg)
15568
warningsStream <- Warnings(warnings)
15669
if err != nil {
@@ -193,3 +106,149 @@ func (actor Actor) Actualize(state PushState, progressBar ProgressBar) (
193106
}()
194107
return stateStream, eventStream, warningsStream, errorStream
195108
}
109+
110+
func (actor Actor) CreateAndUploadApplicationBits(state PushState, progressBar ProgressBar, warningsStream chan Warnings, eventStream chan Event) (v7action.Package, error) {
111+
log.WithField("Path", state.BitsPath).Info(string(CreatingArchive))
112+
113+
eventStream <- CreatingArchive
114+
archivePath, err := actor.SharedActor.ZipDirectoryResources(state.BitsPath, state.AllResources)
115+
if err != nil {
116+
return v7action.Package{}, err
117+
}
118+
defer os.RemoveAll(archivePath)
119+
120+
eventStream <- CreatingPackage
121+
log.WithField("GUID", state.Application.GUID).Info("creating package")
122+
pkg, warnings, err := actor.V7Actor.CreateBitsPackageByApplication(state.Application.GUID)
123+
warningsStream <- Warnings(warnings)
124+
if err != nil {
125+
return v7action.Package{}, err
126+
}
127+
128+
// Uploading package/app bits
129+
for count := 0; count < PushRetries; count++ {
130+
eventStream <- ReadingArchive
131+
log.WithField("GUID", state.Application.GUID).Info("reading archive")
132+
file, size, readErr := actor.SharedActor.ReadArchive(archivePath)
133+
if readErr != nil {
134+
return v7action.Package{}, readErr
135+
}
136+
defer file.Close()
137+
138+
eventStream <- UploadingApplicationWithArchive
139+
progressReader := progressBar.NewProgressBarWrapper(file, size)
140+
pkg, warnings, err = actor.V7Actor.UploadBitsPackage(pkg, state.MatchedResources, progressReader, size)
141+
warningsStream <- Warnings(warnings)
142+
143+
if _, ok := err.(ccerror.PipeSeekError); ok {
144+
eventStream <- RetryUpload
145+
continue
146+
}
147+
break
148+
}
149+
150+
if err != nil {
151+
if e, ok := err.(ccerror.PipeSeekError); ok {
152+
return v7action.Package{}, actionerror.UploadFailedError{Err: e.Err}
153+
}
154+
return v7action.Package{}, err
155+
}
156+
157+
eventStream <- UploadWithArchiveComplete
158+
return pkg, nil
159+
}
160+
161+
func (actor Actor) CreateOrUpdateApplication(state PushState, warningsStream chan Warnings, eventStream chan Event) (PushState, error) {
162+
if state.Application.GUID == "" { // Create
163+
log.WithField("Name", state.Application.Name).Info("creating app")
164+
eventStream <- CreatingApplication
165+
166+
createdApp, warnings, err := actor.V7Actor.CreateApplicationInSpace(state.Application, state.SpaceGUID)
167+
state.Application = createdApp
168+
warningsStream <- Warnings(warnings)
169+
if err != nil {
170+
return state, err
171+
}
172+
173+
eventStream <- CreatedApplication
174+
} else { // Update
175+
log.WithField("Name", state.Application.Name).Info("skipping app creation as it has a GUID")
176+
eventStream <- SkippingApplicationCreation
177+
178+
application, warnings, err := actor.V7Actor.UpdateApplication(state.Application)
179+
state.Application = application
180+
warningsStream <- Warnings(warnings)
181+
if err != nil {
182+
return state, err
183+
}
184+
185+
eventStream <- UpdatedApplication
186+
}
187+
188+
return state, nil
189+
}
190+
191+
func (actor Actor) CreatePackage(state PushState, progressBar ProgressBar, warningsStream chan Warnings, eventStream chan Event) (v7action.Package, error) {
192+
if state.Application.LifecycleType == constant.AppLifecycleTypeDocker {
193+
eventStream <- SetDockerImage
194+
pkg, warnings, err := actor.V7Actor.CreateDockerPackageByApplication(state.Application.GUID, v7action.DockerImageCredentials{
195+
Path: state.Overrides.DockerImage,
196+
Username: state.Overrides.DockerUsername,
197+
Password: state.Overrides.DockerPassword,
198+
})
199+
warningsStream <- Warnings(warnings)
200+
if err != nil {
201+
return v7action.Package{}, err
202+
}
203+
eventStream <- SetDockerImageComplete
204+
return pkg, nil
205+
}
206+
207+
return actor.CreateAndUploadApplicationBits(state, progressBar, warningsStream, eventStream)
208+
}
209+
210+
func (actor Actor) ScaleProcess(state PushState, warningsStream chan Warnings, eventStream chan Event) error {
211+
if state.Overrides.Memory.IsSet {
212+
log.Info("Scaling Web Process")
213+
eventStream <- ScaleWebProcess
214+
215+
process := v7action.Process{
216+
Type: constant.ProcessTypeWeb,
217+
MemoryInMB: state.Overrides.Memory,
218+
}
219+
scaleWarnings, err := actor.V7Actor.ScaleProcessByApplication(state.Application.GUID, process)
220+
warningsStream <- Warnings(scaleWarnings)
221+
if err != nil {
222+
return err
223+
}
224+
eventStream <- ScaleWebProcessComplete
225+
}
226+
227+
return nil
228+
}
229+
230+
func (actor Actor) UpdateProcess(state PushState, warningsStream chan Warnings, eventStream chan Event) error {
231+
if state.Overrides.StartCommand.IsSet || state.Overrides.HealthCheckType != "" {
232+
log.Info("Setting Web Process's Configuration")
233+
eventStream <- SetProcessConfiguration
234+
235+
var process v7action.Process
236+
if state.Overrides.StartCommand.IsSet {
237+
process.Command = state.Overrides.StartCommand
238+
}
239+
if state.Overrides.HealthCheckType != "" {
240+
process.HealthCheckType = state.Overrides.HealthCheckType
241+
process.HealthCheckEndpoint = constant.ProcessHealthCheckEndpointDefault
242+
}
243+
244+
log.WithField("Process", process).Debug("Update process")
245+
warnings, err := actor.V7Actor.UpdateProcessByTypeAndApplication(constant.ProcessTypeWeb, state.Application.GUID, process)
246+
warningsStream <- Warnings(warnings)
247+
if err != nil {
248+
return err
249+
}
250+
eventStream <- SetProcessConfigurationComplete
251+
}
252+
253+
return nil
254+
}

‎actor/v7pushaction/actualize_test.go

+77-1
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ var _ = Describe("Actualize", func() {
203203
})
204204

205205
It("returns an app created event, warnings, and updated state", func() {
206+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatingApplication))
206207
Eventually(warningsStream).Should(Receive(ConsistOf("some-app-warnings")))
207208
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatedApplication))
208209
Eventually(stateStream).Should(Receive(MatchFields(IgnoreExtras,
@@ -212,6 +213,7 @@ var _ = Describe("Actualize", func() {
212213
})
213214

214215
It("creates the application", func() {
216+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatingApplication))
215217
Eventually(fakeV7Actor.CreateApplicationInSpaceCallCount).Should(Equal(1))
216218
passedApp, passedSpaceGUID := fakeV7Actor.CreateApplicationInSpaceArgsForCall(0)
217219
Expect(passedApp).To(Equal(state.Application))
@@ -229,6 +231,7 @@ var _ = Describe("Actualize", func() {
229231
})
230232

231233
It("returns warnings and error", func() {
234+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(CreatingApplication))
232235
Eventually(warningsStream).Should(Receive(ConsistOf("some-app-warnings")))
233236
Eventually(errorStream).Should(Receive(MatchError(expectedErr)))
234237
})
@@ -496,7 +499,80 @@ var _ = Describe("Actualize", func() {
496499
})
497500

498501
Describe("package upload", func() {
499-
When("app bits are provided", func() {
502+
When("docker image is provided", func() {
503+
BeforeEach(func() {
504+
state.Application.LifecycleType = constant.AppLifecycleTypeDocker
505+
state.Overrides.DockerImage = "some-docker-image"
506+
state.Overrides.DockerPassword = "some-docker-password"
507+
state.Overrides.DockerUsername = "some-docker-username"
508+
509+
fakeV7Actor.CreateApplicationInSpaceReturns(
510+
v7action.Application{
511+
GUID: "some-app-guid",
512+
Name: state.Application.Name,
513+
LifecycleType: constant.AppLifecycleTypeDocker,
514+
},
515+
v7action.Warnings{"some-app-warnings"},
516+
nil)
517+
})
518+
519+
When("creating the package is successful", func() {
520+
BeforeEach(func() {
521+
fakeV7Actor.CreateDockerPackageByApplicationReturns(
522+
v7action.Package{GUID: "some-package-guid"},
523+
v7action.Warnings{"some-package-warnings"},
524+
nil)
525+
})
526+
527+
It("sets the docker image", func() {
528+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(SetDockerImage))
529+
Eventually(fakeV7Actor.CreateDockerPackageByApplicationCallCount).Should(Equal(1))
530+
Eventually(warningsStream).Should(Receive(ConsistOf("some-package-warnings")))
531+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(SetDockerImageComplete))
532+
533+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(StartingStaging))
534+
Eventually(fakeV7Actor.StageApplicationPackageCallCount).Should(Equal(1))
535+
536+
appGUID, dockerCredentials := fakeV7Actor.CreateDockerPackageByApplicationArgsForCall(0)
537+
Expect(appGUID).To(Equal("some-app-guid"))
538+
Expect(dockerCredentials).To(MatchFields(IgnoreExtras,
539+
Fields{
540+
"Path": Equal("some-docker-image"),
541+
"Username": Equal("some-docker-username"),
542+
"Password": Equal("some-docker-password"),
543+
}))
544+
545+
Expect(fakeV7Actor.PollPackageArgsForCall(0)).To(MatchFields(IgnoreExtras,
546+
Fields{
547+
"GUID": Equal("some-package-guid"),
548+
}))
549+
})
550+
551+
It("does not create/upload archive", func() {
552+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(Complete))
553+
Expect(fakeSharedActor.ZipDirectoryResourcesCallCount()).To(Equal(0))
554+
Expect(fakeV7Actor.CreateBitsPackageByApplicationCallCount()).To(Equal(0))
555+
})
556+
})
557+
558+
When("creating the package errors", func() {
559+
var someErr error
560+
561+
BeforeEach(func() {
562+
someErr = errors.New("I AM A BANANA")
563+
fakeV7Actor.CreateDockerPackageByApplicationReturns(v7action.Package{}, v7action.Warnings{"some-package-warnings"}, someErr)
564+
})
565+
566+
It("returns errors and warnings", func() {
567+
Eventually(getNextEvent(stateStream, eventStream, warningsStream)).Should(Equal(SetDockerImage))
568+
Eventually(warningsStream).Should(Receive(ConsistOf("some-package-warnings")))
569+
Eventually(errorStream).Should(Receive(MatchError(someErr)))
570+
Consistently(getNextEvent(stateStream, eventStream, warningsStream)).ShouldNot(Equal(SetDockerImageComplete))
571+
})
572+
})
573+
})
574+
575+
When("uploading application bits", func() {
500576
BeforeEach(func() {
501577
state = PushState{
502578
Application: v7action.Application{

‎actor/v7pushaction/event.go

+3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const (
99
CreatedApplication Event = "created application"
1010
CreatedRoutes Event = "created routes"
1111
CreatingAndMappingRoutes Event = "creating and mapping routes"
12+
CreatingApplication Event = "creating application"
1213
CreatingArchive Event = "creating archive"
1314
CreatingPackage Event = "creating package"
1415
PollingBuild Event = "polling build"
@@ -17,6 +18,8 @@ const (
1718
RetryUpload Event = "retry upload"
1819
ScaleWebProcess Event = "scaling the web process"
1920
ScaleWebProcessComplete Event = "scaling the web process complete"
21+
SetDockerImage Event = "setting docker properties"
22+
SetDockerImageComplete Event = "completed setting docker properties"
2023
SetDropletComplete Event = "set droplet complete"
2124
SetProcessConfiguration Event = "setting configuration on the process"
2225
SetProcessConfigurationComplete Event = "completed setting configuration on the process"

‎actor/v7pushaction/push_state.go

+11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"code.cloudfoundry.org/cli/actor/v7action"
99
"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
1010
"code.cloudfoundry.org/cli/types"
11+
log "github.com/sirupsen/logrus"
1112
)
1213

1314
type PushState struct {
@@ -25,6 +26,9 @@ type PushState struct {
2526

2627
type FlagOverrides struct {
2728
Buildpacks []string
29+
DockerImage string
30+
DockerPassword string
31+
DockerUsername string
2832
HealthCheckType string
2933
Memory types.NullUint64
3034
ProvidedAppPath string
@@ -49,12 +53,15 @@ func (actor Actor) Conceptualize(appName string, spaceGUID string, orgGUID strin
4953
err error
5054
)
5155

56+
log.WithFields(log.Fields{"appName": appName, "spaceGUID": spaceGUID}).Info("Application Look Up")
5257
application, warnings, err = actor.V7Actor.GetApplicationByNameAndSpace(appName, spaceGUID)
5358
if _, ok := err.(actionerror.ApplicationNotFoundError); ok {
59+
log.Debug("App not found")
5460
application = v7action.Application{
5561
Name: appName,
5662
}
5763
} else if err != nil {
64+
log.Errorln("Looking up application:", err)
5865
return nil, Warnings(warnings), err
5966
}
6067

@@ -63,6 +70,10 @@ func (actor Actor) Conceptualize(appName string, spaceGUID string, orgGUID strin
6370
application.LifecycleBuildpacks = flagOverrides.Buildpacks
6471
}
6572

73+
if len(flagOverrides.DockerImage) != 0 {
74+
application.LifecycleType = constant.AppLifecycleTypeDocker
75+
}
76+
6677
bitsPath := currentDir
6778
if flagOverrides.ProvidedAppPath != "" {
6879
bitsPath = flagOverrides.ProvidedAppPath

‎actor/v7pushaction/push_state_test.go

+14
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,20 @@ var _ = Describe("Push State", func() {
124124
Expect(states[0].Application.LifecycleBuildpacks).To(ConsistOf("some-buildpack-1", "some-buildpack-2"))
125125
})
126126
})
127+
128+
When("docker image information is provided", func() {
129+
BeforeEach(func() {
130+
flagOverrides.DockerImage = "some-docker-image"
131+
flagOverrides.DockerPassword = "some-docker-password"
132+
flagOverrides.DockerUsername = "some-docker-username"
133+
})
134+
135+
It("sets the buildpacks on the app", func() {
136+
Expect(executeErr).ToNot(HaveOccurred())
137+
Expect(states[0].Application.LifecycleType).To(Equal(constant.AppLifecycleTypeDocker))
138+
Expect(states[0].Application.LifecycleBuildpacks).To(BeEmpty())
139+
})
140+
})
127141
})
128142

129143
When("the application lookup errors", func() {

‎actor/v7pushaction/v7_actor.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
type V7Actor interface {
1313
CreateApplicationInSpace(app v7action.Application, spaceGUID string) (v7action.Application, v7action.Warnings, error)
1414
CreateBitsPackageByApplication(appGUID string) (v7action.Package, v7action.Warnings, error)
15+
CreateDockerPackageByApplication(appGUID string, dockerImageCredentials v7action.DockerImageCredentials) (v7action.Package, v7action.Warnings, error)
1516
GetApplicationByNameAndSpace(appName string, spaceGUID string) (v7action.Application, v7action.Warnings, error)
1617
PollBuild(buildGUID string, appName string) (v7action.Droplet, v7action.Warnings, error)
1718
PollPackage(pkg v7action.Package) (v7action.Package, v7action.Warnings, error)

‎actor/v7pushaction/v7pushactionfakes/fake_v7actor.go

+85
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎command/v7/push_command.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ func (cmd PushCommand) processEvent(appName string, event v7pushaction.Event) bo
270270
cmd.UI.DisplayTextWithFlavor("Updating app {{.AppName}}...", map[string]interface{}{
271271
"AppName": appName,
272272
})
273-
case v7pushaction.CreatedApplication:
273+
case v7pushaction.CreatingApplication:
274274
cmd.UI.DisplayTextWithFlavor("Creating app {{.AppName}}...", map[string]interface{}{
275275
"AppName": appName,
276276
})
@@ -328,8 +328,20 @@ func (cmd PushCommand) getLogs(logStream <-chan *v7action.LogMessage, errStream
328328
}
329329

330330
func (cmd PushCommand) GetFlagOverrides() (v7pushaction.FlagOverrides, error) {
331+
var dockerPassword string
332+
if cmd.DockerUsername != "" {
333+
var err error
334+
dockerPassword, err = cmd.UI.DisplayPasswordPrompt("Docker password")
335+
if err != nil {
336+
return v7pushaction.FlagOverrides{}, err
337+
}
338+
}
339+
331340
return v7pushaction.FlagOverrides{
332341
Buildpacks: cmd.Buildpacks,
342+
DockerImage: cmd.DockerImage.Path,
343+
DockerUsername: cmd.DockerUsername,
344+
DockerPassword: dockerPassword,
333345
HealthCheckType: cmd.HealthCheckType.Type,
334346
Memory: cmd.Memory.NullUint64,
335347
ProvidedAppPath: string(cmd.AppPath),

‎command/v7/push_command_test.go

+37-3
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ func ReturnLogs(logevents []LogEvent, passedWarnings v7action.Warnings, passedEr
9292
var _ = Describe("push Command", func() {
9393
var (
9494
cmd PushCommand
95+
input *Buffer
9596
testUI *ui.UI
9697
fakeConfig *commandfakes.FakeConfig
9798
fakeSharedActor *commandfakes.FakeSharedActor
@@ -110,7 +111,8 @@ var _ = Describe("push Command", func() {
110111
)
111112

112113
BeforeEach(func() {
113-
testUI = ui.NewTestUI(nil, NewBuffer(), NewBuffer())
114+
input = NewBuffer()
115+
testUI = ui.NewTestUI(input, NewBuffer(), NewBuffer())
114116
fakeConfig = new(commandfakes.FakeConfig)
115117
fakeSharedActor = new(commandfakes.FakeSharedActor)
116118
fakeActor = new(v7fakes.FakePushActor)
@@ -213,7 +215,7 @@ var _ = Describe("push Command", func() {
213215
Warnings: v7pushaction.Warnings{"skipping app creation warnings"},
214216
},
215217
{
216-
Event: v7pushaction.CreatedApplication,
218+
Event: v7pushaction.CreatingApplication,
217219
Warnings: v7pushaction.Warnings{"app creation warnings"},
218220
},
219221
{
@@ -547,12 +549,44 @@ var _ = Describe("push Command", func() {
547549
Expect(overridesErr).ToNot(HaveOccurred())
548550
})
549551

550-
It("sets them on the command line settings", func() {
552+
It("sets them on the flag overrides", func() {
551553
Expect(overridesErr).ToNot(HaveOccurred())
552554
Expect(overrides.Buildpacks).To(ConsistOf("buildpack-1", "buildpack-2"))
553555
Expect(overrides.HealthCheckType).To(Equal("port"))
554556
Expect(overrides.Memory).To(Equal(types.NullUint64{Value: 100, IsSet: true}))
555557
Expect(overrides.StartCommand).To(Equal(types.FilteredString{IsSet: true, Value: "some-start-command"}))
556558
})
559+
560+
When("a docker image is provided", func() {
561+
BeforeEach(func() {
562+
cmd.DockerImage = flag.DockerImage{Path: "some-docker-image"}
563+
})
564+
565+
It("sets docker image on the flag overrides", func() {
566+
Expect(overridesErr).ToNot(HaveOccurred())
567+
Expect(overrides.DockerImage).To(Equal("some-docker-image"))
568+
})
569+
570+
When("docker username is provided", func() {
571+
When("a password is provided via environment variable", func() {})
572+
573+
When("no password is provided", func() {
574+
BeforeEach(func() {
575+
cmd.DockerUsername = "some-docker-username"
576+
input.Write([]byte("some-docker-password\n"))
577+
})
578+
579+
It("prompts for a password", func() {
580+
Expect(overridesErr).ToNot(HaveOccurred())
581+
582+
// Expect(testUI.Out).To(Say("Environment variable CF_DOCKER_PASSWORD not set."))
583+
Expect(testUI.Out).To(Say("Docker password"))
584+
585+
Expect(overrides.DockerUsername).To(Equal("some-docker-username"))
586+
Expect(overrides.DockerPassword).To(Equal("some-docker-password"))
587+
})
588+
})
589+
})
590+
})
557591
})
558592
})

‎integration/v7/push/docker_test.go

+23-18
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
1-
// +build !partialPush
2-
31
package push
42

53
import (
6-
"os"
7-
84
"code.cloudfoundry.org/cli/integration/helpers"
95
. "github.com/onsi/ginkgo"
106
. "github.com/onsi/gomega"
@@ -51,16 +47,10 @@ var _ = Describe("pushing docker images", func() {
5147
)
5248

5349
BeforeEach(func() {
54-
privateDockerImage = os.Getenv("CF_INT_DOCKER_IMAGE")
55-
privateDockerUsername = os.Getenv("CF_INT_DOCKER_USERNAME")
56-
privateDockerPassword = os.Getenv("CF_INT_DOCKER_PASSWORD")
57-
58-
if privateDockerImage == "" || privateDockerUsername == "" || privateDockerPassword == "" {
59-
Skip("CF_INT_DOCKER_IMAGE, CF_INT_DOCKER_USERNAME, or CF_INT_DOCKER_PASSWORD is not set")
60-
}
50+
privateDockerImage, privateDockerUsername, privateDockerPassword = helpers.SkipIfPrivateDockerInfoNotSet()
6151
})
6252

63-
When("the docker passwored is provided via environment variable", func() {
53+
PWhen("the docker passwored is provided via environment variable", func() {
6454
It("uses the specified private docker image", func() {
6555
session := helpers.CustomCF(
6656
helpers.CFEnv{
@@ -71,6 +61,7 @@ var _ = Describe("pushing docker images", func() {
7161
"--docker-image", privateDockerImage,
7262
)
7363

64+
Consistently(session).ShouldNot(Say("Docker password"))
7465
Eventually(session).Should(Say(`name:\s+%s`, appName))
7566
Eventually(session).Should(Say(`requested state:\s+started`))
7667
Eventually(session).Should(Say("stack:"))
@@ -81,16 +72,30 @@ var _ = Describe("pushing docker images", func() {
8172
})
8273

8374
When("the docker passwored is not provided", func() {
84-
It("returns an error", func() {
85-
session := helpers.CF(
86-
PushCommandName, appName,
75+
var buffer *Buffer
76+
77+
BeforeEach(func() {
78+
buffer = NewBuffer()
79+
_, err := buffer.Write([]byte(privateDockerPassword + "\n"))
80+
Expect(err).NotTo(HaveOccurred())
81+
})
82+
83+
It("prompts for the docker password", func() {
84+
session := helpers.CFWithStdin(buffer,
85+
PushCommandName,
86+
appName,
8787
"--docker-username", privateDockerUsername,
8888
"--docker-image", privateDockerImage,
8989
)
9090

91-
Eventually(session.Err).Should(Say("Environment variable CF_DOCKER_PASSWORD not set."))
92-
Eventually(session).Should(Say("FAILED"))
93-
Eventually(session).Should(Exit(1))
91+
// Eventually(session).Should(Say("Environment variable CF_DOCKER_PASSWORD not set."))
92+
Eventually(session).Should(Say("Docker password"))
93+
Eventually(session).Should(Say(`name:\s+%s`, appName))
94+
Eventually(session).Should(Say(`requested state:\s+started`))
95+
Eventually(session).Should(Say("stack:"))
96+
Consistently(session).ShouldNot(Say("buildpacks:"))
97+
Eventually(session).Should(Say(`docker image:\s+%s`, privateDockerImage))
98+
Eventually(session).Should(Exit(0))
9499
})
95100
})
96101
})

0 commit comments

Comments
 (0)
Please sign in to comment.