Skip to content

Commit e69040a

Browse files
c0d1ngm0nk3ymodulo11pbuskonicolasbender
committed
Add cnb lifecycle type
Co-authored-by: Johannes Dillmann <j.dillmann@sap.com> Co-authored-by: Pavel Busko <pavel.busko@sap.com> Co-authored-by: Nicolas Bender <nicolas.bender@sap.com>
1 parent 8acaca1 commit e69040a

File tree

13 files changed

+249
-26
lines changed

13 files changed

+249
-26
lines changed

actor/v7pushaction/actor.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ func NewActor(v3Actor V7Actor, sharedActor SharedActor) *Actor {
4343
HandleInstancesOverride,
4444
HandleStartCommandOverride,
4545

46+
HandleLifecycleOverride,
47+
4648
// Type must come before endpoint because endpoint validates against type
4749
HandleHealthCheckTypeOverride,
4850
HandleHealthCheckEndpointOverride,

actor/v7pushaction/create_push_plans.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ func (actor Actor) CreatePushPlans(
3131
BitsPath: manifestApplication.Path,
3232
}
3333

34+
if manifestApplication.Lifecycle != "" {
35+
plan.Application.LifecycleType = manifestApplication.Lifecycle
36+
}
37+
38+
if overrides.Lifecycle != "" {
39+
plan.Application.LifecycleType = overrides.Lifecycle
40+
}
41+
3442
if manifestApplication.Docker != nil {
3543
plan.DockerImageCredentials = v7action.DockerImageCredentials{
3644
Path: manifestApplication.Docker.Image,

actor/v7pushaction/create_push_plans_test.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ var _ = Describe("CreatePushPlans", func() {
4141

4242
manifest = manifestparser.Manifest{
4343
Applications: []manifestparser.Application{
44-
{Name: "name-1", Path: "path1"},
44+
{Name: "name-1", Path: "path1", Lifecycle: "cnb"},
4545
{Name: "name-2", Path: "path2", Docker: &manifestparser.Docker{Image: "image", Username: "uname"}},
4646
},
4747
}
@@ -120,6 +120,7 @@ var _ = Describe("CreatePushPlans", func() {
120120
Expect(pushPlans[0].DockerImageCredentials.Username).To(Equal(""))
121121
Expect(pushPlans[0].DockerImageCredentials.Password).To(Equal(""))
122122
Expect(pushPlans[0].BitsPath).To(Equal("path1"))
123+
Expect(pushPlans[0].Application.LifecycleType).To(BeEquivalentTo("cnb"))
123124
Expect(pushPlans[1].Application.Name).To(Equal("name-2"))
124125
Expect(pushPlans[1].Application.GUID).To(Equal("app-guid-2"))
125126
Expect(pushPlans[1].SpaceGUID).To(Equal(spaceGUID))
@@ -128,7 +129,20 @@ var _ = Describe("CreatePushPlans", func() {
128129
Expect(pushPlans[1].DockerImageCredentials.Username).To(Equal("uname"))
129130
Expect(pushPlans[1].DockerImageCredentials.Password).To(Equal("passwd"))
130131
Expect(pushPlans[1].BitsPath).To(Equal("path2"))
132+
Expect(pushPlans[1].Application.LifecycleType).To(BeEquivalentTo(""))
133+
})
134+
})
135+
136+
When("lifecycle is overwritten", func() {
137+
BeforeEach(func() {
138+
flagOverrides = FlagOverrides{
139+
Lifecycle: "buildpack",
140+
}
131141
})
132142

143+
It("uses the lifecycle from the command line", func() {
144+
Expect(pushPlans[0].Application.LifecycleType).To(BeEquivalentTo("buildpack"))
145+
})
133146
})
147+
134148
})
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package v7pushaction
2+
3+
import (
4+
"code.cloudfoundry.org/cli/command/translatableerror"
5+
"code.cloudfoundry.org/cli/util/manifestparser"
6+
)
7+
8+
func HandleLifecycleOverride(manifest manifestparser.Manifest, overrides FlagOverrides) (manifestparser.Manifest, error) {
9+
if overrides.Lifecycle != "" {
10+
if manifest.ContainsMultipleApps() {
11+
return manifest, translatableerror.CommandLineArgsWithMultipleAppsError{}
12+
}
13+
14+
app := manifest.GetFirstApp()
15+
16+
app.Lifecycle = overrides.Lifecycle
17+
}
18+
19+
return manifest, nil
20+
}

actor/v7pushaction/push_plan.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type FlagOverrides struct {
6060
NoManifest bool
6161
Task bool
6262
LogRateLimit string
63+
Lifecycle constant.AppLifecycleType
6364
}
6465

6566
func (state PushPlan) String() string {

api/cloudcontroller/ccv3/application_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,17 @@ var _ = Describe("Application", func() {
9696
})
9797
})
9898

99+
When("lifecycle type cnb is provided", func() {
100+
BeforeEach(func() {
101+
app.LifecycleType = constant.AppLifecycleTypeCNB
102+
app.LifecycleBuildpacks = []string{"docker://nodejs"}
103+
})
104+
105+
It("sets the lifecycle buildpack to be empty in the JSON", func() {
106+
Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"data":{"buildpacks":["docker://nodejs"]},"type":"cnb"}}`))
107+
})
108+
})
109+
99110
When("null buildpack is provided", func() {
100111
BeforeEach(func() {
101112
app.LifecycleBuildpacks = []string{"null"}

api/cloudcontroller/ccv3/constant/application.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ const (
99
// AppLifecycleTypeDocker will pull a docker image from a registry to run an
1010
// app.
1111
AppLifecycleTypeDocker AppLifecycleType = "docker"
12+
// AppLifecycleTypeCNB will use a droplet (created with cloud native buildpacks)
13+
// and a rootfs to run the app.
14+
AppLifecycleTypeCNB AppLifecycleType = "cnb"
1215
)
1316

1417
// ApplicationAction represents the action being taken on an application

cf/commands/application/apps.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"code.cloudfoundry.org/cli/cf/flags"
88
. "code.cloudfoundry.org/cli/cf/i18n"
99
"code.cloudfoundry.org/cli/cf/models"
10-
"code.cloudfoundry.org/cli/plugin/models"
10+
plugin_models "code.cloudfoundry.org/cli/plugin/models"
1111

1212
"code.cloudfoundry.org/cli/cf/api"
1313
"code.cloudfoundry.org/cli/cf/configuration/coreconfig"

command/v7/create_app_command.go

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package v7
22

33
import (
4+
"errors"
5+
"fmt"
6+
47
"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
58
"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
69
"code.cloudfoundry.org/cli/command/flag"
@@ -11,8 +14,9 @@ type CreateAppCommand struct {
1114
BaseCommand
1215

1316
RequiredArgs flag.AppName `positional-args:"yes"`
14-
AppType flag.AppType `long:"app-type" choice:"buildpack" choice:"docker" description:"App lifecycle type to stage and run the app" default:"buildpack"`
15-
usage interface{} `usage:"CF_NAME create-app APP_NAME [--app-type (buildpack | docker)]"`
17+
AppType flag.AppType `long:"app-type" choice:"buildpack" choice:"docker" choice:"cnb" description:"App lifecycle type to stage and run the app" default:"buildpack"`
18+
Buildpacks []string `long:"buildpack" short:"b" description:"Custom buildpack by name (e.g. my-buildpack), Docker image (e.g. docker://registry/image:tag), Git URL (e.g. 'https://github.com/cloudfoundry/java-buildpack.git') or Git URL with a branch or tag (e.g. 'https://github.com/cloudfoundry/java-buildpack.git#v3.3.0' for 'v3.3.0' tag). To use built-in buildpacks only, specify 'default' or 'null'"`
19+
usage interface{} `usage:"CF_NAME create-app APP_NAME [--app-type (buildpack | docker | cnb)]"`
1620
relatedCommands interface{} `related_commands:"app, apps, push"`
1721
}
1822

@@ -34,11 +38,20 @@ func (cmd CreateAppCommand) Execute(args []string) error {
3438
"CurrentUser": user.Name,
3539
})
3640

41+
cmd.UI.DisplayText(fmt.Sprintf("Using app type %q", constant.AppLifecycleType(cmd.AppType)))
42+
43+
app := resources.Application{
44+
Name: cmd.RequiredArgs.AppName,
45+
LifecycleType: constant.AppLifecycleType(cmd.AppType),
46+
LifecycleBuildpacks: cmd.Buildpacks,
47+
}
48+
49+
if constant.AppLifecycleType(cmd.AppType) == constant.AppLifecycleTypeCNB && len(cmd.Buildpacks) == 0 {
50+
return errors.New("buildpack(s) must be provided when using --app-type cnb")
51+
}
52+
3753
_, warnings, err := cmd.Actor.CreateApplicationInSpace(
38-
resources.Application{
39-
Name: cmd.RequiredArgs.AppName,
40-
LifecycleType: constant.AppLifecycleType(cmd.AppType),
41-
},
54+
app,
4255
cmd.Config.TargetedSpace().GUID,
4356
)
4457
cmd.UI.DisplayWarnings(warnings)

command/v7/push_command.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,9 @@ type PushCommand struct {
104104
Task bool `long:"task" description:"Push an app that is used only to execute tasks. The app will be staged, but not started and will have no route assigned."`
105105
Vars []template.VarKV `long:"var" description:"Variable key value pair for variable substitution, (e.g., name=app1); can specify multiple times"`
106106
PathsToVarsFiles []flag.PathWithExistenceCheck `long:"vars-file" description:"Path to a variable substitution file for manifest; can specify multiple times"`
107+
Lifecycle constant.AppLifecycleType `long:"lifecycle" description:"App lifecycle type to stage and run the app" default:""`
107108
dockerPassword interface{} `environmentName:"CF_DOCKER_PASSWORD" environmentDescription:"Password used for private docker repository"`
108-
usage interface{} `usage:"CF_NAME push APP_NAME [-b BUILDPACK_NAME]\n [-c COMMAND] [-f MANIFEST_PATH | --no-manifest] [--no-start] [--no-wait] [-i NUM_INSTANCES]\n [-k DISK] [-m MEMORY] [-l LOG_RATE_LIMIT] [-p PATH] [-s STACK] [-t HEALTH_TIMEOUT] [--task TASK]\n [-u (process | port | http)] [--no-route | --random-route]\n [--var KEY=VALUE] [--vars-file VARS_FILE_PATH]...\n \n CF_NAME push APP_NAME --docker-image [REGISTRY_HOST:PORT/]IMAGE[:TAG] [--docker-username USERNAME]\n [-c COMMAND] [-f MANIFEST_PATH | --no-manifest] [--no-start] [--no-wait] [-i NUM_INSTANCES]\n [-k DISK] [-m MEMORY] [-l LOG_RATE_LIMIT] [-p PATH] [-s STACK] [-t HEALTH_TIMEOUT] [--task TASK]\n [-u (process | port | http)] [--no-route | --random-route ]\n [--var KEY=VALUE] [--vars-file VARS_FILE_PATH]..."`
109+
usage interface{} `usage:"CF_NAME push APP_NAME [-b BUILDPACK_NAME]\n [-c COMMAND] [-f MANIFEST_PATH | --no-manifest] [--lifecycle (buildpack | docker | cnb)] [--no-start] [--no-wait] [-i NUM_INSTANCES]\n [-k DISK] [-m MEMORY] [-l LOG_RATE_LIMIT] [-p PATH] [-s STACK] [-t HEALTH_TIMEOUT] [--task TASK]\n [-u (process | port | http)] [--no-route | --random-route]\n [--var KEY=VALUE] [--vars-file VARS_FILE_PATH]...\n \n CF_NAME push APP_NAME --docker-image [REGISTRY_HOST:PORT/]IMAGE[:TAG] [--docker-username USERNAME]\n [-c COMMAND] [-f MANIFEST_PATH | --no-manifest] [--no-start] [--no-wait] [-i NUM_INSTANCES]\n [-k DISK] [-m MEMORY] [-l LOG_RATE_LIMIT] [-p PATH] [-s STACK] [-t HEALTH_TIMEOUT] [--task TASK]\n [-u (process | port | http)] [--no-route | --random-route ]\n [--var KEY=VALUE] [--vars-file VARS_FILE_PATH]..."`
109110
envCFStagingTimeout interface{} `environmentName:"CF_STAGING_TIMEOUT" environmentDescription:"Max wait time for staging, in minutes" environmentDefault:"15"`
110111
envCFStartupTimeout interface{} `environmentName:"CF_STARTUP_TIMEOUT" environmentDescription:"Max wait time for app instance startup, in minutes" environmentDefault:"5"`
111112

@@ -237,6 +238,7 @@ func (cmd PushCommand) Execute(args []string) error {
237238
transformedManifest,
238239
flagOverrides,
239240
)
241+
240242
cmd.UI.DisplayWarnings(warnings)
241243
if err != nil {
242244
return err
@@ -361,11 +363,73 @@ func (cmd PushCommand) GetFlagOverrides() (v7pushaction.FlagOverrides, error) {
361363
NoManifest: cmd.NoManifest,
362364
Task: cmd.Task,
363365
LogRateLimit: cmd.LogRateLimit,
366+
Lifecycle: cmd.Lifecycle,
364367
}, nil
365368
}
366369

367370
func (cmd PushCommand) ValidateFlags() error {
368371
switch {
372+
// Lifecycle buildpack requested
373+
374+
case cmd.Lifecycle == constant.AppLifecycleTypeBuildpack && cmd.DockerImage.Path != "":
375+
return translatableerror.ArgumentCombinationError{
376+
Args: []string{
377+
"--lifecycle buildpack",
378+
"--docker-image, -o",
379+
},
380+
}
381+
382+
case cmd.Lifecycle == constant.AppLifecycleTypeBuildpack && cmd.DockerUsername != "":
383+
return translatableerror.ArgumentCombinationError{
384+
Args: []string{
385+
"--lifecycle buildpack",
386+
"--docker-username",
387+
},
388+
}
389+
390+
// Lifecycle docker requested
391+
392+
case cmd.Lifecycle == constant.AppLifecycleTypeDocker && cmd.Buildpacks != nil:
393+
return translatableerror.ArgumentCombinationError{
394+
Args: []string{
395+
"--lifecycle docker",
396+
"--buildpack, -b",
397+
},
398+
}
399+
400+
case cmd.Lifecycle == constant.AppLifecycleTypeDocker && cmd.Stack != "":
401+
return translatableerror.ArgumentCombinationError{
402+
Args: []string{
403+
"--lifecycle docker",
404+
"--stack, -s",
405+
},
406+
}
407+
case cmd.Lifecycle == constant.AppLifecycleTypeDocker && cmd.DropletPath != "":
408+
return translatableerror.ArgumentCombinationError{
409+
Args: []string{
410+
"--lifecycle docker",
411+
"--droplet",
412+
},
413+
}
414+
415+
// Lifecycle cnb requested
416+
417+
case cmd.Lifecycle == constant.AppLifecycleTypeCNB && cmd.DockerImage.Path != "":
418+
return translatableerror.ArgumentCombinationError{
419+
Args: []string{
420+
"--lifecycle cnb",
421+
"--docker-image, -o",
422+
},
423+
}
424+
425+
case cmd.Lifecycle == constant.AppLifecycleTypeCNB && cmd.DockerUsername != "":
426+
return translatableerror.ArgumentCombinationError{
427+
Args: []string{
428+
"--lifecycle cnb",
429+
"--docker-username",
430+
},
431+
}
432+
369433
case cmd.DockerUsername != "" && cmd.DockerImage.Path == "":
370434
return translatableerror.RequiredFlagsError{
371435
Arg1: "--docker-image, -o",

0 commit comments

Comments
 (0)