Skip to content

Commit 16007ba

Browse files
committed
[DRAFT] Use embedded process instances for "cf apps" summary
* see cloudfoundry/cloud_controller_ng#4796
1 parent 4f56e82 commit 16007ba

10 files changed

Lines changed: 348 additions & 33 deletions

File tree

actor/v7action/application_summary.go

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

33
import (
44
"errors"
5+
"fmt"
6+
"time"
57

68
"code.cloudfoundry.org/cli/v8/actor/actionerror"
9+
"code.cloudfoundry.org/cli/v8/actor/versioncheck"
710
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccerror"
811
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccv3"
12+
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccv3/constant"
13+
"code.cloudfoundry.org/cli/v8/api/cloudcontroller/ccversion"
914
"code.cloudfoundry.org/cli/v8/resources"
1015
"code.cloudfoundry.org/cli/v8/util/batcher"
1116
)
@@ -49,6 +54,7 @@ func (actor Actor) GetAppSummariesForSpace(spaceGUID string, labelSelector strin
4954
keys = append(keys, ccv3.Query{Key: ccv3.LabelSelectorFilter, Values: []string{labelSelector}})
5055
}
5156
apps, ccv3Warnings, err := actor.CloudControllerClient.GetApplications(keys...)
57+
fmt.Println(fmt.Sprintf("GetApplications returned: %d", len(apps)))
5258
allWarnings = append(allWarnings, ccv3Warnings...)
5359
if err != nil {
5460
return nil, allWarnings, err
@@ -58,7 +64,16 @@ func (actor Actor) GetAppSummariesForSpace(spaceGUID string, labelSelector strin
5864
var warnings Warnings
5965

6066
if !omitStats {
61-
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForApps(apps)
67+
embeddedProcessInstancesAvailable, versionErr := versioncheck.IsMinimumAPIVersionMet(actor.Config.APIVersion(), ccversion.MinVersionEmbeddedProcessInstances)
68+
if versionErr != nil {
69+
return nil, allWarnings, versionErr
70+
}
71+
fmt.Println(fmt.Sprintf("embeddedProcessInstancesAvailable: %v", embeddedProcessInstancesAvailable))
72+
if embeddedProcessInstancesAvailable {
73+
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForSpace(spaceGUID)
74+
} else {
75+
processSummariesByAppGUID, warnings, err = actor.getProcessSummariesForApps(apps)
76+
}
6277
allWarnings = append(allWarnings, warnings...)
6378
if err != nil {
6479
return nil, allWarnings, err
@@ -174,6 +189,40 @@ func (actor Actor) getProcessSummariesForApps(apps []resources.Application) (map
174189

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

actor/v7action/application_summary_test.go

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,32 @@ import (
1212
"code.cloudfoundry.org/cli/v8/resources"
1313
"code.cloudfoundry.org/cli/v8/types"
1414
"code.cloudfoundry.org/clock"
15+
"github.com/onsi/gomega/format"
1516

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

1819
. "github.com/onsi/ginkgo/v2"
1920
. "github.com/onsi/gomega"
2021
)
2122

23+
var _ = BeforeSuite(func() {
24+
// Controls how long Gomega prints values in failure messages.
25+
// 0 means "no limit" (prints full values).
26+
format.MaxLength = 0 // or e.g. 20000
27+
})
28+
2229
var _ = Describe("Application Summary Actions", func() {
2330
var (
2431
actor *Actor
2532
fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
33+
fakeConfig *v7actionfakes.FakeConfig
2634
)
2735

2836
BeforeEach(func() {
2937
fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient)
30-
actor = NewActor(fakeCloudControllerClient, nil, nil, nil, nil, clock.NewClock())
38+
fakeConfig = new(v7actionfakes.FakeConfig)
39+
fakeConfig.APIVersionReturns("3.210.0")
40+
actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, clock.NewClock())
3141
})
3242

3343
Describe("ApplicationSummary", func() {
@@ -287,6 +297,155 @@ var _ = Describe("Application Summary Actions", func() {
287297
Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("some-process-guid"))
288298
})
289299

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

actor/v7action/cloud_controller_client.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ type CloudControllerClient interface {
6868
GetApplicationEnvironment(appGUID string) (ccv3.Environment, ccv3.Warnings, error)
6969
GetApplicationManifest(appGUID string) ([]byte, ccv3.Warnings, error)
7070
GetApplicationProcessByType(appGUID string, processType string) (resources.Process, ccv3.Warnings, error)
71-
GetApplicationProcesses(appGUID string) ([]resources.Process, ccv3.Warnings, error)
71+
GetApplicationProcesses(appGUID string, query ...ccv3.Query) ([]resources.Process, ccv3.Warnings, error)
7272
GetApplicationRevisions(appGUID string, query ...ccv3.Query) ([]resources.Revision, ccv3.Warnings, error)
7373
GetApplicationRevisionsDeployed(appGUID string) ([]resources.Revision, ccv3.Warnings, error)
7474
GetApplicationRoutes(appGUID string) ([]resources.Route, ccv3.Warnings, error)

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

actor/v7action/v7actionfakes/fake_cloud_controller_client.go

Lines changed: 10 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/cloudcontroller/ccv3/process.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,13 @@ func (client *Client) GetApplicationProcessByType(appGUID string, processType st
3535

3636
// GetApplicationProcesses lists processes for a given application. **Note**:
3737
// Due to security, the API obfuscates certain values such as `command`.
38-
func (client *Client) GetApplicationProcesses(appGUID string) ([]resources.Process, Warnings, error) {
38+
func (client *Client) GetApplicationProcesses(appGUID string, query ...Query) ([]resources.Process, Warnings, error) {
3939
var processes []resources.Process
4040

4141
_, warnings, err := client.MakeListRequest(RequestParams{
4242
RequestName: internal.GetApplicationProcessesRequest,
4343
URIParams: internal.Params{"app_guid": appGUID},
44+
Query: query,
4445
ResponseBody: resources.Process{},
4546
AppendToList: func(item interface{}) error {
4647
processes = append(processes, item.(resources.Process))

0 commit comments

Comments
 (0)