Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support docker lifecycle when creating apps #2816

Merged
merged 1 commit into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 61 additions & 4 deletions api/handlers/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,6 @@ var _ = Describe("App", func() {
})

It("returns the App", func() {
Expect(appRepo.CreateAppCallCount()).To(Equal(1))
_, actualAuthInfo, _ := appRepo.CreateAppArgsForCall(0)
Expect(actualAuthInfo).To(Equal(authInfo))

Expect(rr).To(HaveHTTPStatus(http.StatusCreated))
Expect(rr).To(HaveHTTPHeaderWithValue("Content-Type", "application/json"))

Expand All @@ -168,6 +164,67 @@ var _ = Describe("App", func() {
)))
})

It("sends an AppCreate message with default lifecycle to the repository", func() {
Expect(appRepo.CreateAppCallCount()).To(Equal(1))
_, actualAuthInfo, actualCreateMessage := appRepo.CreateAppArgsForCall(0)
Expect(actualAuthInfo).To(Equal(authInfo))
Expect(actualCreateMessage).To(Equal(repositories.CreateAppMessage{
Name: appName,
SpaceGUID: spaceGUID,
State: "STOPPED",
Lifecycle: repositories.Lifecycle{
Type: "buildpack",
Data: repositories.LifecycleData{
Stack: "cflinuxfs3",
},
},
}))
})

When("the app has buildpack lifecycle", func() {
BeforeEach(func() {
payload.Lifecycle = &payloads.Lifecycle{
Type: "buildpack",
Data: &payloads.LifecycleData{
Buildpacks: []string{"bp1"},
Stack: "my-stack",
},
}
})

It("sends an AppCreate message with buildpack lifecycle", func() {
Expect(appRepo.CreateAppCallCount()).To(Equal(1))
_, _, actualCreateMessage := appRepo.CreateAppArgsForCall(0)
Expect(actualCreateMessage.Lifecycle).To(Equal(
repositories.Lifecycle{
Type: "buildpack",
Data: repositories.LifecycleData{
Stack: "my-stack",
Buildpacks: []string{"bp1"},
},
}))
})
})

When("the app has docker lifecycle", func() {
BeforeEach(func() {
payload.Lifecycle = &payloads.Lifecycle{
Type: "docker",
Data: &payloads.LifecycleData{},
}
})

It("sends an AppCreate message with docker lifecycle and empty data", func() {
Expect(appRepo.CreateAppCallCount()).To(Equal(1))
_, _, actualCreateMessage := appRepo.CreateAppArgsForCall(0)
Expect(actualCreateMessage.Lifecycle).To(Equal(
repositories.Lifecycle{
Type: "docker",
Data: repositories.LifecycleData{},
}))
})
})

It("creates the `web` process", func() {
Expect(processRepo.CreateProcessCallCount()).To(Equal(1))
_, actualAuthInfo, actualMsg := processRepo.CreateProcessArgsForCall(0)
Expand Down
1 change: 1 addition & 0 deletions api/payloads/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func (p AppCreate) ToAppCreateMessage() repositories.CreateAppMessage {
},
}
if p.Lifecycle != nil {
lifecycleBlock.Type = p.Lifecycle.Type
lifecycleBlock.Data.Stack = p.Lifecycle.Data.Stack
lifecycleBlock.Data.Buildpacks = p.Lifecycle.Data.Buildpacks
}
Expand Down
214 changes: 154 additions & 60 deletions api/payloads/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,96 +65,190 @@ var _ = Describe("App payload validation", func() {
var validatorErr error

Describe("AppCreate", func() {
var (
payload payloads.AppCreate
decodedPayload *payloads.AppCreate
)
Describe("Decoding", func() {
var (
payload payloads.AppCreate
decodedPayload *payloads.AppCreate
)

BeforeEach(func() {
payload = payloads.AppCreate{
Name: "my-app",
Relationships: &payloads.AppRelationships{
Space: &payloads.Relationship{
Data: &payloads.RelationshipData{
GUID: "app-guid",
BeforeEach(func() {
payload = payloads.AppCreate{
Name: "my-app",
Relationships: &payloads.AppRelationships{
Space: &payloads.Relationship{
Data: &payloads.RelationshipData{
GUID: "app-guid",
},
},
},
},
}

decodedPayload = new(payloads.AppCreate)
})

JustBeforeEach(func() {
validatorErr = validator.DecodeAndValidateJSONPayload(createJSONRequest(payload), decodedPayload)
})
}

It("succeeds", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(decodedPayload).To(gstruct.PointTo(Equal(payload)))
})
decodedPayload = new(payloads.AppCreate)
})

When("name is not set", func() {
BeforeEach(func() {
payload.Name = ""
JustBeforeEach(func() {
validatorErr = validator.DecodeAndValidateJSONPayload(createJSONRequest(payload), decodedPayload)
})

It("returns an error", func() {
expectUnprocessableEntityError(validatorErr, "name cannot be blank")
It("succeeds", func() {
Expect(validatorErr).NotTo(HaveOccurred())
Expect(decodedPayload).To(gstruct.PointTo(Equal(payload)))
})
})

When("name is invalid", func() {
BeforeEach(func() {
payload.Name = "!@#"
When("name is not set", func() {
BeforeEach(func() {
payload.Name = ""
})

It("returns an error", func() {
expectUnprocessableEntityError(validatorErr, "name cannot be blank")
})
})

It("returns an error", func() {
expectUnprocessableEntityError(validatorErr, "name must consist only of letters, numbers, underscores and dashes")
When("name is invalid", func() {
BeforeEach(func() {
payload.Name = "!@#"
})

It("returns an error", func() {
expectUnprocessableEntityError(validatorErr, "name must consist only of letters, numbers, underscores and dashes")
})
})
})

When("lifecycle is invalid", func() {
BeforeEach(func() {
payload.Lifecycle = &payloads.Lifecycle{}
When("lifecycle is invalid", func() {
BeforeEach(func() {
payload.Lifecycle = &payloads.Lifecycle{}
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(validatorErr, "lifecycle.type cannot be blank")
})
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(validatorErr, "lifecycle.type cannot be blank")
When("relationships are not set", func() {
BeforeEach(func() {
payload.Relationships = nil
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(validatorErr, "relationships is required")
})
})
})

When("relationships are not set", func() {
BeforeEach(func() {
payload.Relationships = nil
When("relationships space is not set", func() {
BeforeEach(func() {
payload.Relationships.Space = nil
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(validatorErr, "relationships.space is required")
})
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(validatorErr, "relationships is required")
When("metadata is invalid", func() {
BeforeEach(func() {
payload.Metadata = payloads.Metadata{
Labels: map[string]string{
"foo.cloudfoundry.org/bar": "jim",
},
}
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "label/annotation key cannot use the cloudfoundry.org domain")
})
})
})

When("relationships space is not set", func() {
Describe("ToAppCreateMessage", func() {
var (
payload payloads.AppCreate
repoMessage repositories.CreateAppMessage
)

BeforeEach(func() {
payload.Relationships.Space = nil
payload = payloads.AppCreate{
Name: "app-name",
EnvironmentVariables: map[string]string{
"foo": "bar",
},
Relationships: &payloads.AppRelationships{
Space: &payloads.Relationship{
Data: &payloads.RelationshipData{
GUID: "space-guid",
},
},
},
Metadata: payloads.Metadata{
Labels: map[string]string{
"l1": "v1",
},
},
}
})

It("returns an unprocessable entity error", func() {
expectUnprocessableEntityError(validatorErr, "relationships.space is required")
JustBeforeEach(func() {
repoMessage = payload.ToAppCreateMessage()
})
})

When("metadata is invalid", func() {
BeforeEach(func() {
payload.Metadata = payloads.Metadata{
Labels: map[string]string{
"foo.cloudfoundry.org/bar": "jim",
It("creates an app create message with default lifecycle", func() {
Expect(repoMessage).To(Equal(repositories.CreateAppMessage{
Name: "app-name",
SpaceGUID: "space-guid",
State: repositories.StoppedState,
Lifecycle: repositories.Lifecycle{
Type: "buildpack",
Data: repositories.LifecycleData{
Stack: "cflinuxfs3",
},
},
}
EnvironmentVariables: map[string]string{
"foo": "bar",
},
Metadata: repositories.Metadata{
Labels: map[string]string{
"l1": "v1",
},
},
}))
})

It("returns an appropriate error", func() {
expectUnprocessableEntityError(validatorErr, "label/annotation key cannot use the cloudfoundry.org domain")
When("the lifecycle is buildpack", func() {
BeforeEach(func() {
payload.Lifecycle = &payloads.Lifecycle{
Type: "buildpack",
Data: &payloads.LifecycleData{
Buildpacks: []string{"my-bp"},
Stack: "my-stack",
},
}
})

It("sets the lifecycle to the repo message", func() {
Expect(repoMessage.Lifecycle).To(Equal(repositories.Lifecycle{
Type: "buildpack",
Data: repositories.LifecycleData{
Buildpacks: []string{"my-bp"},
Stack: "my-stack",
},
}))
})
})

When("the lifecycle is docker", func() {
BeforeEach(func() {
payload.Lifecycle = &payloads.Lifecycle{
Type: "docker",
Data: &payloads.LifecycleData{},
}
})

It("sets the lifecycle to the repo message", func() {
Expect(repoMessage.Lifecycle).To(Equal(repositories.Lifecycle{
Type: "docker",
Data: repositories.LifecycleData{},
}))
})
})
})
})
Expand Down
Loading
Loading