Skip to content
Draft
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
48 changes: 47 additions & 1 deletion actor/v7action/application_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ package v7action

import (
"errors"
"time"

"code.cloudfoundry.org/cli/v8/actor/actionerror"
"code.cloudfoundry.org/cli/v8/actor/versioncheck"
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccerror"
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccv3"
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccv3/constant"
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccversion"
"code.cloudfoundry.org/cli/v8/resources"
"code.cloudfoundry.org/cli/v8/util/batcher"
)
Expand Down Expand Up @@ -58,7 +62,15 @@ func (actor Actor) GetAppSummariesForSpace(spaceGUID string, labelSelector strin
var warnings Warnings

if !omitStats {
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForApps(apps)
embeddedProcessInstancesAvailable, versionErr := versioncheck.IsMinimumAPIVersionMet(actor.Config.APIVersion(), ccversion.MinVersionEmbeddedProcessInstances)
if versionErr != nil {
return nil, allWarnings, versionErr
}
if embeddedProcessInstancesAvailable {
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForSpace(spaceGUID)
} else {
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForApps(apps)
}
allWarnings = append(allWarnings, warnings...)
if err != nil {
return nil, allWarnings, err
Expand Down Expand Up @@ -174,6 +186,40 @@ func (actor Actor) getProcessSummariesForApps(apps []resources.Application) (map

processSummariesByAppGUID[process.AppGUID] = append(processSummariesByAppGUID[process.AppGUID], processSummary)
}

return processSummariesByAppGUID, allWarnings, nil
}

func (actor Actor) getProcessSummariesForSpace(spaceGUID string) (map[string]ProcessSummaries, Warnings, error) {
processSummariesByAppGUID := make(map[string]ProcessSummaries)
var allWarnings Warnings
var processes []resources.Process

// use "/v3/processes?space_guids=:guid&embed=process_instances" to get processes and process instances in one request
processes, warnings, err := actor.CloudControllerClient.GetProcesses(
ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}},
ccv3.Query{Key: ccv3.Embed, Values: []string{"process_instances"}},
)
allWarnings = append(allWarnings, warnings...)
if err != nil {
return nil, allWarnings, err
}

for _, process := range processes {
var instanceDetails []ProcessInstance
if process.EmbeddedProcessInstances != nil {
for _, instance := range *process.EmbeddedProcessInstances {
instanceDetails = append(instanceDetails, NewProcessInstance(instance.Index, constant.ProcessInstanceState(instance.State), time.Duration(instance.Since)))
}
}
processSummary := ProcessSummary{
Process: process,
InstanceDetails: instanceDetails,
}

processSummariesByAppGUID[process.AppGUID] = append(processSummariesByAppGUID[process.AppGUID], processSummary)

}
return processSummariesByAppGUID, allWarnings, nil
}

Expand Down
154 changes: 151 additions & 3 deletions actor/v7action/application_summary_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"fmt"

"code.cloudfoundry.org/cli/v8/actor/actionerror"
. "code.cloudfoundry.org/cli/v8/actor/v7action"
"code.cloudfoundry.org/cli/v8/actor/v7action/v7actionfakes"
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccerror"
Expand All @@ -13,8 +14,6 @@ import (
"code.cloudfoundry.org/cli/v8/types"
"code.cloudfoundry.org/clock"

"code.cloudfoundry.org/cli/v8/actor/actionerror"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
Expand All @@ -23,11 +22,14 @@ var _ = Describe("Application Summary Actions", func() {
var (
actor *Actor
fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
fakeConfig *v7actionfakes.FakeConfig
)

BeforeEach(func() {
fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient)
actor = NewActor(fakeCloudControllerClient, nil, nil, nil, nil, clock.NewClock())
fakeConfig = new(v7actionfakes.FakeConfig)
fakeConfig.APIVersionReturns("3.210.0")
actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, clock.NewClock())
})

Describe("ApplicationSummary", func() {
Expand Down Expand Up @@ -287,6 +289,152 @@ var _ = Describe("Application Summary Actions", func() {
Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("some-process-guid"))
})

Context("the cloud controller supports embedded process instances", func() {
BeforeEach(func() {
fakeConfig.APIVersionReturns("3.211.0")

listedProcesses := []resources.Process{
{
GUID: "some-process-guid",
Type: "some-type",
Command: *types.NewFilteredString("[Redacted Value]"),
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
AppGUID: "some-app-guid",
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
{Index: 0, State: "RUNNING", Since: 300},
{Index: 1, State: "CRASHED", Since: 0},
},
},
{
GUID: "some-process-web-guid",
Type: "web",
Command: *types.NewFilteredString("[Redacted Value]"),
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
AppGUID: "some-app-guid",
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
{Index: 0, State: "RUNNING", Since: 500},
{Index: 1, State: "RUNNING", Since: 600},
},
},
}

fakeCloudControllerClient.GetProcessesReturns(
listedProcesses,
ccv3.Warnings{"get-space-processes-warning"},
nil,
)
})

It("uses the embedded process instances", func() {
Expect(executeErr).ToNot(HaveOccurred())
Expect(summaries).To(Equal([]ApplicationSummary{
{
Application: resources.Application{
Name: "some-app-name",
GUID: "some-app-guid",
State: constant.ApplicationStarted,
},
ProcessSummaries: []ProcessSummary{
{
Process: resources.Process{
GUID: "some-process-web-guid",
Type: "web",
Command: *types.NewFilteredString("[Redacted Value]"),
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
AppGUID: "some-app-guid",
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
{Index: 0, State: "RUNNING", Since: 500},
{Index: 1, State: "RUNNING", Since: 600},
},
},
InstanceDetails: []ProcessInstance{
{
Index: 0,
State: "RUNNING",
Uptime: 500,
},
{
Index: 1,
State: "RUNNING",
Uptime: 600,
},
},
},
{
Process: resources.Process{
GUID: "some-process-guid",
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
Type: "some-type",
Command: *types.NewFilteredString("[Redacted Value]"),
AppGUID: "some-app-guid",
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
{Index: 0, State: "RUNNING", Since: 300},
{Index: 1, State: "CRASHED", Since: 0},
},
},
InstanceDetails: []ProcessInstance{
{
Index: 0,
State: "RUNNING",
Uptime: 300,
},
{
Index: 1,
State: "CRASHED",
Uptime: 0,
},
},
},
},
Routes: []resources.Route{
{
GUID: "some-route-guid",
Destinations: []resources.RouteDestination{
{
App: resources.RouteDestinationApp{
GUID: "some-app-guid",
},
},
},
},
{
GUID: "some-other-route-guid",
Destinations: []resources.RouteDestination{
{
App: resources.RouteDestinationApp{
GUID: "some-app-guid",
},
},
},
},
},
},
}))

Expect(warnings).To(ConsistOf(
"get-apps-warning",
"get-space-processes-warning",
"get-routes-warning",
))

Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
ccv3.Query{Key: ccv3.OrderBy, Values: []string{"name"}},
ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
ccv3.Query{Key: ccv3.LabelSelectorFilter, Values: []string{"some-key=some-value"}},
ccv3.Query{Key: ccv3.PerPage, Values: []string{ccv3.MaxPerPage}},
))

Expect(fakeCloudControllerClient.GetProcessesCallCount()).To(Equal(1))
Expect(fakeCloudControllerClient.GetProcessesArgsForCall(0)).To(ConsistOf(
ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
ccv3.Query{Key: ccv3.Embed, Values: []string{"process_instances"}},
))

Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(0))
})
})

When("there is no label selector", func() {
BeforeEach(func() {
labelSelector = ""
Expand Down
8 changes: 8 additions & 0 deletions actor/v7action/process_instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import (

type ProcessInstance ccv3.ProcessInstance

func NewProcessInstance(index int64, state constant.ProcessInstanceState, uptime time.Duration) ProcessInstance {
return ProcessInstance(ccv3.ProcessInstance{
Index: index,
State: state,
Uptime: uptime,
})
}

// Running will return true if the instance is running.
func (instance ProcessInstance) Running() bool {
return instance.State == constant.ProcessInstanceRunning
Expand Down
49 changes: 27 additions & 22 deletions api/cloudcontroller/ccv3/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ var _ = Describe("Process", func() {
"ReadinessHealthCheckEndpoint": Equal("/foo"),
"ReadinessHealthCheckInvocationTimeout": BeEquivalentTo(2),
"ReadinessHealthCheckInterval": BeEquivalentTo(9),
"EmbeddedProcessInstances": BeNil(),
}))
})
})
Expand Down Expand Up @@ -367,6 +368,7 @@ var _ = Describe("Process", func() {
"ReadinessHealthCheckEndpoint": Equal("/foo"),
"ReadinessHealthCheckInvocationTimeout": BeEquivalentTo(2),
"ReadinessHealthCheckInterval": BeEquivalentTo(9),
"EmbeddedProcessInstances": BeNil(),
}))
})
})
Expand Down Expand Up @@ -524,32 +526,35 @@ var _ = Describe("Process", func() {

Expect(processes).To(ConsistOf(
resources.Process{
GUID: "process-1-guid",
Type: constant.ProcessTypeWeb,
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
LogRateLimitInBPS: types.NullInt{Value: 64, IsSet: true},
HealthCheckType: constant.Port,
HealthCheckTimeout: 0,
GUID: "process-1-guid",
Type: constant.ProcessTypeWeb,
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
LogRateLimitInBPS: types.NullInt{Value: 64, IsSet: true},
HealthCheckType: constant.Port,
HealthCheckTimeout: 0,
EmbeddedProcessInstances: nil,
},
resources.Process{
GUID: "process-2-guid",
Type: "worker",
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
LogRateLimitInBPS: types.NullInt{Value: 128, IsSet: true},
HealthCheckType: constant.HTTP,
HealthCheckEndpoint: "/health",
HealthCheckTimeout: 60,
GUID: "process-2-guid",
Type: "worker",
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
LogRateLimitInBPS: types.NullInt{Value: 128, IsSet: true},
HealthCheckType: constant.HTTP,
HealthCheckEndpoint: "/health",
HealthCheckTimeout: 60,
EmbeddedProcessInstances: nil,
},
resources.Process{
GUID: "process-3-guid",
Type: "console",
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
MemoryInMB: types.NullUint64{Value: 128, IsSet: true},
LogRateLimitInBPS: types.NullInt{Value: 256, IsSet: true},
HealthCheckType: constant.Process,
HealthCheckTimeout: 90,
GUID: "process-3-guid",
Type: "console",
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
MemoryInMB: types.NullUint64{Value: 128, IsSet: true},
LogRateLimitInBPS: types.NullInt{Value: 256, IsSet: true},
HealthCheckType: constant.Process,
HealthCheckTimeout: 90,
EmbeddedProcessInstances: nil,
},
))
Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
Expand Down
2 changes: 2 additions & 0 deletions api/cloudcontroller/ccv3/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ const (
// Include is a query parameter for specifying other resources associated with the
// resource returned by the endpoint
Include QueryKey = "include"
// see https://v3-apidocs.cloudfoundry.org/version/3.212.0/index.html#embed
Embed QueryKey = "embed"

// GloballyEnabledStaging is the query parameter for getting only security groups that are globally enabled for staging
GloballyEnabledStaging QueryKey = "globally_enabled_staging"
Expand Down
2 changes: 2 additions & 0 deletions api/cloudcontroller/ccversion/minimum_version.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ const (
MinVersionServiceBindingStrategy = "3.205.0"

MinVersionUpdateStack = "3.210.0"

MinVersionEmbeddedProcessInstances = "3.211.0"
)
Loading
Loading