Skip to content

Commit dea08a9

Browse files
committed
Use embedded process instances for "cf apps" summary
* use "/v3/processes?space_guids=:guid&embed=process_instances" to get processes and process instances in one request * see cloudfoundry/cloud_controller_ng#4796
1 parent 4f56e82 commit dea08a9

File tree

7 files changed

+259
-28
lines changed

7 files changed

+259
-28
lines changed

actor/v7action/application_summary.go

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ package v7action
22

33
import (
44
"errors"
5+
"time"
56

67
"code.cloudfoundry.org/cli/v8/actor/actionerror"
8+
"code.cloudfoundry.org/cli/v8/actor/versioncheck"
79
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccerror"
810
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccv3"
11+
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccv3/constant"
12+
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccversion"
913
"code.cloudfoundry.org/cli/v8/resources"
1014
"code.cloudfoundry.org/cli/v8/util/batcher"
1115
)
@@ -58,7 +62,15 @@ func (actor Actor) GetAppSummariesForSpace(spaceGUID string, labelSelector strin
5862
var warnings Warnings
5963

6064
if !omitStats {
61-
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForApps(apps)
65+
embeddedProcessInstancesAvailable, versionErr := versioncheck.IsMinimumAPIVersionMet(actor.Config.APIVersion(), ccversion.MinVersionEmbeddedProcessInstances)
66+
if versionErr != nil {
67+
return nil, allWarnings, versionErr
68+
}
69+
if embeddedProcessInstancesAvailable {
70+
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForSpace(spaceGUID)
71+
} else {
72+
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForApps(apps)
73+
}
6274
allWarnings = append(allWarnings, warnings...)
6375
if err != nil {
6476
return nil, allWarnings, err
@@ -174,6 +186,40 @@ func (actor Actor) getProcessSummariesForApps(apps []resources.Application) (map
174186

175187
processSummariesByAppGUID[process.AppGUID] = append(processSummariesByAppGUID[process.AppGUID], processSummary)
176188
}
189+
190+
return processSummariesByAppGUID, allWarnings, nil
191+
}
192+
193+
func (actor Actor) getProcessSummariesForSpace(spaceGUID string) (map[string]ProcessSummaries, Warnings, error) {
194+
processSummariesByAppGUID := make(map[string]ProcessSummaries)
195+
var allWarnings Warnings
196+
var processes []resources.Process
197+
198+
// use "/v3/processes?space_guids=:guid&embed=process_instances" to get processes and process instances in one request
199+
processes, warnings, err := actor.CloudControllerClient.GetProcesses(
200+
ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}},
201+
ccv3.Query{Key: ccv3.Embed, Values: []string{"process_instances"}},
202+
)
203+
allWarnings = append(allWarnings, warnings...)
204+
if err != nil {
205+
return nil, allWarnings, err
206+
}
207+
208+
for _, process := range processes {
209+
var instanceDetails []ProcessInstance
210+
if process.EmbeddedProcessInstances != nil {
211+
for _, instance := range *process.EmbeddedProcessInstances {
212+
instanceDetails = append(instanceDetails, NewProcessInstance(instance.Index, constant.ProcessInstanceState(instance.State), time.Duration(instance.Since)))
213+
}
214+
}
215+
processSummary := ProcessSummary{
216+
Process: process,
217+
InstanceDetails: instanceDetails,
218+
}
219+
220+
processSummariesByAppGUID[process.AppGUID] = append(processSummariesByAppGUID[process.AppGUID], processSummary)
221+
222+
}
177223
return processSummariesByAppGUID, allWarnings, nil
178224
}
179225

actor/v7action/application_summary_test.go

Lines changed: 151 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66

7+
"code.cloudfoundry.org/cli/v8/actor/actionerror"
78
. "code.cloudfoundry.org/cli/v8/actor/v7action"
89
"code.cloudfoundry.org/cli/v8/actor/v7action/v7actionfakes"
910
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccerror"
@@ -13,8 +14,6 @@ import (
1314
"code.cloudfoundry.org/cli/v8/types"
1415
"code.cloudfoundry.org/clock"
1516

16-
"code.cloudfoundry.org/cli/v8/actor/actionerror"
17-
1817
. "github.com/onsi/ginkgo/v2"
1918
. "github.com/onsi/gomega"
2019
)
@@ -23,11 +22,14 @@ var _ = Describe("Application Summary Actions", func() {
2322
var (
2423
actor *Actor
2524
fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
25+
fakeConfig *v7actionfakes.FakeConfig
2626
)
2727

2828
BeforeEach(func() {
2929
fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient)
30-
actor = NewActor(fakeCloudControllerClient, nil, nil, nil, nil, clock.NewClock())
30+
fakeConfig = new(v7actionfakes.FakeConfig)
31+
fakeConfig.APIVersionReturns("3.210.0")
32+
actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, clock.NewClock())
3133
})
3234

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

292+
Context("the cloud controller supports embedded process instances", func() {
293+
BeforeEach(func() {
294+
fakeConfig.APIVersionReturns("3.211.0")
295+
296+
listedProcesses := []resources.Process{
297+
{
298+
GUID: "some-process-guid",
299+
Type: "some-type",
300+
Command: *types.NewFilteredString("[Redacted Value]"),
301+
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
302+
AppGUID: "some-app-guid",
303+
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
304+
{Index: 0, State: "RUNNING", Since: 300},
305+
{Index: 1, State: "CRASHED", Since: 0},
306+
},
307+
},
308+
{
309+
GUID: "some-process-web-guid",
310+
Type: "web",
311+
Command: *types.NewFilteredString("[Redacted Value]"),
312+
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
313+
AppGUID: "some-app-guid",
314+
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
315+
{Index: 0, State: "RUNNING", Since: 500},
316+
{Index: 1, State: "RUNNING", Since: 600},
317+
},
318+
},
319+
}
320+
321+
fakeCloudControllerClient.GetProcessesReturns(
322+
listedProcesses,
323+
ccv3.Warnings{"get-space-processes-warning"},
324+
nil,
325+
)
326+
})
327+
328+
It("uses the embedded process instances", func() {
329+
Expect(executeErr).ToNot(HaveOccurred())
330+
Expect(summaries).To(Equal([]ApplicationSummary{
331+
{
332+
Application: resources.Application{
333+
Name: "some-app-name",
334+
GUID: "some-app-guid",
335+
State: constant.ApplicationStarted,
336+
},
337+
ProcessSummaries: []ProcessSummary{
338+
{
339+
Process: resources.Process{
340+
GUID: "some-process-web-guid",
341+
Type: "web",
342+
Command: *types.NewFilteredString("[Redacted Value]"),
343+
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
344+
AppGUID: "some-app-guid",
345+
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
346+
{Index: 0, State: "RUNNING", Since: 500},
347+
{Index: 1, State: "RUNNING", Since: 600},
348+
},
349+
},
350+
InstanceDetails: []ProcessInstance{
351+
{
352+
Index: 0,
353+
State: "RUNNING",
354+
Uptime: 500,
355+
},
356+
{
357+
Index: 1,
358+
State: "RUNNING",
359+
Uptime: 600,
360+
},
361+
},
362+
},
363+
{
364+
Process: resources.Process{
365+
GUID: "some-process-guid",
366+
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
367+
Type: "some-type",
368+
Command: *types.NewFilteredString("[Redacted Value]"),
369+
AppGUID: "some-app-guid",
370+
EmbeddedProcessInstances: &[]resources.EmbeddedProcessInstance{
371+
{Index: 0, State: "RUNNING", Since: 300},
372+
{Index: 1, State: "CRASHED", Since: 0},
373+
},
374+
},
375+
InstanceDetails: []ProcessInstance{
376+
{
377+
Index: 0,
378+
State: "RUNNING",
379+
Uptime: 300,
380+
},
381+
{
382+
Index: 1,
383+
State: "CRASHED",
384+
Uptime: 0,
385+
},
386+
},
387+
},
388+
},
389+
Routes: []resources.Route{
390+
{
391+
GUID: "some-route-guid",
392+
Destinations: []resources.RouteDestination{
393+
{
394+
App: resources.RouteDestinationApp{
395+
GUID: "some-app-guid",
396+
},
397+
},
398+
},
399+
},
400+
{
401+
GUID: "some-other-route-guid",
402+
Destinations: []resources.RouteDestination{
403+
{
404+
App: resources.RouteDestinationApp{
405+
GUID: "some-app-guid",
406+
},
407+
},
408+
},
409+
},
410+
},
411+
},
412+
}))
413+
414+
Expect(warnings).To(ConsistOf(
415+
"get-apps-warning",
416+
"get-space-processes-warning",
417+
"get-routes-warning",
418+
))
419+
420+
Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
421+
Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
422+
ccv3.Query{Key: ccv3.OrderBy, Values: []string{"name"}},
423+
ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
424+
ccv3.Query{Key: ccv3.LabelSelectorFilter, Values: []string{"some-key=some-value"}},
425+
ccv3.Query{Key: ccv3.PerPage, Values: []string{ccv3.MaxPerPage}},
426+
))
427+
428+
Expect(fakeCloudControllerClient.GetProcessesCallCount()).To(Equal(1))
429+
Expect(fakeCloudControllerClient.GetProcessesArgsForCall(0)).To(ConsistOf(
430+
ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
431+
ccv3.Query{Key: ccv3.Embed, Values: []string{"process_instances"}},
432+
))
433+
434+
Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(0))
435+
})
436+
})
437+
290438
When("there is no label selector", func() {
291439
BeforeEach(func() {
292440
labelSelector = ""

actor/v7action/process_instance.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@ import (
1212

1313
type ProcessInstance ccv3.ProcessInstance
1414

15+
func NewProcessInstance(index int64, state constant.ProcessInstanceState, uptime time.Duration) ProcessInstance {
16+
return ProcessInstance(ccv3.ProcessInstance{
17+
Index: index,
18+
State: state,
19+
Uptime: uptime,
20+
})
21+
}
22+
1523
// Running will return true if the instance is running.
1624
func (instance ProcessInstance) Running() bool {
1725
return instance.State == constant.ProcessInstanceRunning

api/cloudcontroller/ccv3/process_test.go

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ var _ = Describe("Process", func() {
9494
"ReadinessHealthCheckEndpoint": Equal("/foo"),
9595
"ReadinessHealthCheckInvocationTimeout": BeEquivalentTo(2),
9696
"ReadinessHealthCheckInterval": BeEquivalentTo(9),
97+
"EmbeddedProcessInstances": BeNil(),
9798
}))
9899
})
99100
})
@@ -367,6 +368,7 @@ var _ = Describe("Process", func() {
367368
"ReadinessHealthCheckEndpoint": Equal("/foo"),
368369
"ReadinessHealthCheckInvocationTimeout": BeEquivalentTo(2),
369370
"ReadinessHealthCheckInterval": BeEquivalentTo(9),
371+
"EmbeddedProcessInstances": BeNil(),
370372
}))
371373
})
372374
})
@@ -524,32 +526,35 @@ var _ = Describe("Process", func() {
524526

525527
Expect(processes).To(ConsistOf(
526528
resources.Process{
527-
GUID: "process-1-guid",
528-
Type: constant.ProcessTypeWeb,
529-
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
530-
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
531-
LogRateLimitInBPS: types.NullInt{Value: 64, IsSet: true},
532-
HealthCheckType: constant.Port,
533-
HealthCheckTimeout: 0,
529+
GUID: "process-1-guid",
530+
Type: constant.ProcessTypeWeb,
531+
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
532+
MemoryInMB: types.NullUint64{Value: 32, IsSet: true},
533+
LogRateLimitInBPS: types.NullInt{Value: 64, IsSet: true},
534+
HealthCheckType: constant.Port,
535+
HealthCheckTimeout: 0,
536+
EmbeddedProcessInstances: nil,
534537
},
535538
resources.Process{
536-
GUID: "process-2-guid",
537-
Type: "worker",
538-
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
539-
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
540-
LogRateLimitInBPS: types.NullInt{Value: 128, IsSet: true},
541-
HealthCheckType: constant.HTTP,
542-
HealthCheckEndpoint: "/health",
543-
HealthCheckTimeout: 60,
539+
GUID: "process-2-guid",
540+
Type: "worker",
541+
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
542+
MemoryInMB: types.NullUint64{Value: 64, IsSet: true},
543+
LogRateLimitInBPS: types.NullInt{Value: 128, IsSet: true},
544+
HealthCheckType: constant.HTTP,
545+
HealthCheckEndpoint: "/health",
546+
HealthCheckTimeout: 60,
547+
EmbeddedProcessInstances: nil,
544548
},
545549
resources.Process{
546-
GUID: "process-3-guid",
547-
Type: "console",
548-
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
549-
MemoryInMB: types.NullUint64{Value: 128, IsSet: true},
550-
LogRateLimitInBPS: types.NullInt{Value: 256, IsSet: true},
551-
HealthCheckType: constant.Process,
552-
HealthCheckTimeout: 90,
550+
GUID: "process-3-guid",
551+
Type: "console",
552+
Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"},
553+
MemoryInMB: types.NullUint64{Value: 128, IsSet: true},
554+
LogRateLimitInBPS: types.NullInt{Value: 256, IsSet: true},
555+
HealthCheckType: constant.Process,
556+
HealthCheckTimeout: 90,
557+
EmbeddedProcessInstances: nil,
553558
},
554559
))
555560
Expect(warnings).To(ConsistOf("warning-1", "warning-2"))

api/cloudcontroller/ccv3/query.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ const (
101101
// Include is a query parameter for specifying other resources associated with the
102102
// resource returned by the endpoint
103103
Include QueryKey = "include"
104+
// see https://v3-apidocs.cloudfoundry.org/version/3.212.0/index.html#embed
105+
Embed QueryKey = "embed"
104106

105107
// GloballyEnabledStaging is the query parameter for getting only security groups that are globally enabled for staging
106108
GloballyEnabledStaging QueryKey = "globally_enabled_staging"

api/cloudcontroller/ccversion/minimum_version.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ const (
2626
MinVersionServiceBindingStrategy = "3.205.0"
2727

2828
MinVersionUpdateStack = "3.210.0"
29+
30+
MinVersionEmbeddedProcessInstances = "3.211.0"
2931
)

0 commit comments

Comments
 (0)