Skip to content

Commit 6e74a77

Browse files
authored
[v8] Use embedded process instances for "cf apps" summary (#3725)
* 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 * Fix instance.Uptime time conversion * and remove unnecessary pointer for EmbeddedProcessInstances slice
1 parent 3141537 commit 6e74a77

7 files changed

Lines changed: 260 additions & 28 deletions

File tree

actor/v7action/application_summary.go

Lines changed: 49 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,42 @@ 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+
for _, instance := range process.EmbeddedProcessInstances {
211+
instanceDetails = append(instanceDetails, NewProcessInstance(
212+
instance.Index,
213+
constant.ProcessInstanceState(instance.State),
214+
time.Duration(instance.Since)*time.Second,
215+
))
216+
}
217+
processSummary := ProcessSummary{
218+
Process: process,
219+
InstanceDetails: instanceDetails,
220+
}
221+
222+
processSummariesByAppGUID[process.AppGUID] = append(processSummariesByAppGUID[process.AppGUID], processSummary)
223+
224+
}
177225
return processSummariesByAppGUID, allWarnings, nil
178226
}
179227

actor/v7action/application_summary_test.go

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package v7action_test
33
import (
44
"errors"
55
"fmt"
6+
"time"
67

8+
"code.cloudfoundry.org/cli/v8/actor/actionerror"
79
. "code.cloudfoundry.org/cli/v8/actor/v7action"
810
"code.cloudfoundry.org/cli/v8/actor/v7action/v7actionfakes"
911
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccerror"
@@ -13,8 +15,6 @@ import (
1315
"code.cloudfoundry.org/cli/v8/types"
1416
"code.cloudfoundry.org/clock"
1517

16-
"code.cloudfoundry.org/cli/v8/actor/actionerror"
17-
1818
. "github.com/onsi/ginkgo/v2"
1919
. "github.com/onsi/gomega"
2020
)
@@ -23,11 +23,14 @@ var _ = Describe("Application Summary Actions", func() {
2323
var (
2424
actor *Actor
2525
fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
26+
fakeConfig *v7actionfakes.FakeConfig
2627
)
2728

2829
BeforeEach(func() {
2930
fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient)
30-
actor = NewActor(fakeCloudControllerClient, nil, nil, nil, nil, clock.NewClock())
31+
fakeConfig = new(v7actionfakes.FakeConfig)
32+
fakeConfig.APIVersionReturns("3.210.0")
33+
actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, clock.NewClock())
3134
})
3235

3336
Describe("ApplicationSummary", func() {
@@ -287,6 +290,152 @@ var _ = Describe("Application Summary Actions", func() {
287290
Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("some-process-guid"))
288291
})
289292

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

2626
MinVersionServiceBindingStrategy = "3.205.0"
2727

28+
MinVersionEmbeddedProcessInstances = "3.211.0"
29+
2830
MinVersionUpdateStack = "3.211.0"
2931
)

0 commit comments

Comments
 (0)