From a119c1f95f8c29ae3b1eab3f83b84d22b247a151 Mon Sep 17 00:00:00 2001 From: Paul Lorenz Date: Tue, 28 Apr 2020 14:55:22 -0400 Subject: [PATCH] Add paging information to CLI commands --- doc/release-notes.md | 14 ++ ziti/cmd/ziti/cmd/edge_controller/common.go | 6 +- .../cmd/edge_controller/create_edge_router.go | 2 +- .../cmd/edge_controller/create_identity.go | 2 +- ziti/cmd/ziti/cmd/edge_controller/list.go | 161 ++++++++++++------ .../cmd/edge_controller/policy_advisor.go | 8 +- 6 files changed, 133 insertions(+), 60 deletions(-) diff --git a/doc/release-notes.md b/doc/release-notes.md index cb45f0183..86cd90799 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -1,3 +1,17 @@ +# Release 0.13.9 +## Theme + * Adds paging information to cli commands + + Example + + ```shell script +$ ec list api-sessions "true sort by token skip 2 limit 3" +id: 37dd1463-e4e7-40de-9a63-f75486430361 token: 0b392a2f-47f8-4561-af63-93807ce70d93 identity: Default Admin +id: 6fb5b488-debf-4212-9670-f250e31b3d4f token: 15ae6b00-f123-458c-a121-5cf91983a2c2 identity: Default Admin +id: 8aa4a074-b2c7-4d55-9f56-17199ab6ac11 token: 1b9418d8-b9a7-4e39-a876-7a9588f5e7ed identity: Default Admin +results: 3-5 of 23 +``` + # Release 0.13.8 ## Theme * Fixes Ziti Edge Router ignoring connect options for SDK listener diff --git a/ziti/cmd/ziti/cmd/edge_controller/common.go b/ziti/cmd/ziti/cmd/edge_controller/common.go index ede232643..3ea7d451e 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/common.go +++ b/ziti/cmd/ziti/cmd/edge_controller/common.go @@ -32,7 +32,7 @@ func mapNameToID(entityType string, val string) (string, error) { // Allow UUID formatted names to be recognized with a name: prefix name := strings.TrimPrefix(val, "name:") - list, err := filterEntitiesOfType(entityType, fmt.Sprintf("name=\"%s\"", name), false, nil) + list, _, err := filterEntitiesOfType(entityType, fmt.Sprintf("name=\"%s\"", name), false, nil) if err != nil { return "", err } @@ -52,7 +52,7 @@ func mapNameToID(entityType string, val string) (string, error) { } func mapIdToName(entityType string, val string) (string, error) { - list, err := filterEntitiesOfType(entityType, fmt.Sprintf(`id="%s"`, val), false, nil) + list, _, err := filterEntitiesOfType(entityType, fmt.Sprintf(`id="%s"`, val), false, nil) if err != nil { return "", err } @@ -77,7 +77,7 @@ func mapNamesToIDs(entityType string, list ...string) ([]string, error) { } else { // Allow UUID formatted names to be recognized with a name: prefix name := strings.TrimPrefix(val, "name:") - list, err := filterEntitiesOfType(entityType, fmt.Sprintf("name=\"%s\"", name), false, nil) + list, _, err := filterEntitiesOfType(entityType, fmt.Sprintf("name=\"%s\"", name), false, nil) if err != nil { return nil, err } diff --git a/ziti/cmd/ziti/cmd/edge_controller/create_edge_router.go b/ziti/cmd/ziti/cmd/edge_controller/create_edge_router.go index 4017b7d4c..35a687a9f 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/create_edge_router.go +++ b/ziti/cmd/ziti/cmd/edge_controller/create_edge_router.go @@ -95,7 +95,7 @@ func runCreateEdgeRouter(o *createEdgeRouterOptions) error { } func getEdgeRouterJwt(o *createEdgeRouterOptions, id string) error { - list, err := listEntitiesOfType("edge-routers", nil, o.OutputJSONResponse, o.Out) + list, _, err := listEntitiesOfType("edge-routers", nil, o.OutputJSONResponse, o.Out) if err != nil { return err } diff --git a/ziti/cmd/ziti/cmd/edge_controller/create_identity.go b/ziti/cmd/ziti/cmd/edge_controller/create_identity.go index f80dff0cb..356c94763 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/create_identity.go +++ b/ziti/cmd/ziti/cmd/edge_controller/create_identity.go @@ -115,7 +115,7 @@ func runCreateIdentity(idType string, o *createIdentityOptions) error { } func getIdentityJwt(o *createIdentityOptions, id string) error { - list, err := listEntitiesOfType("identities", nil, o.OutputJSONResponse, o.Out) + list, _, err := listEntitiesOfType("identities", nil, o.OutputJSONResponse, o.Out) if err != nil { return err } diff --git a/ziti/cmd/ziti/cmd/edge_controller/list.go b/ziti/cmd/ziti/cmd/edge_controller/list.go index 7b4d5d5ed..d637f82f7 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/list.go +++ b/ziti/cmd/ziti/cmd/edge_controller/list.go @@ -19,8 +19,11 @@ package edge_controller import ( "encoding/json" "fmt" + "github.com/netfoundry/ziti-foundation/util/errorz" + "github.com/pkg/errors" "io" "net/url" + "reflect" "github.com/Jeffail/gabs" "github.com/netfoundry/ziti-cmd/ziti/cmd/ziti/cmd/common" @@ -110,9 +113,31 @@ func newListCmd(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comma return cmd } +type paging struct { + limit int64 + offset int64 + count int64 + errorz.ErrorHolderImpl +} + +func (p *paging) output(o *commonOptions) { + if p.HasError() { + _, _ = fmt.Fprintf(o.Out, "unable to retrieve paging information: %v\n", p.Err) + } else if p.count == 0 { + _, _ = fmt.Fprintln(o.Out, "results: none") + } else { + first := p.offset + 1 + last := p.offset + p.limit + if last > p.count { + last = p.count + } + _, _ = fmt.Fprintf(o.Out, "results: %v-%v of %v\n", first, last, p.count) + } +} + type listCommandRunner func(*commonOptions) error -type outputFunction func(o *commonOptions, children []*gabs.Container) error +type outputFunction func(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error func newEntityListRootCmd(entityType string) *cobra.Command { desc := fmt.Sprintf("list entities related to a %v instance managed by the Ziti Edge Controller", entityType) @@ -268,44 +293,73 @@ func newSubListCmdForEntityType(entityType string, subType string, outputF outpu } // listEntitiesOfType queries the Ziti Controller for entities of the given type -func listEntitiesWithOptions(entityType string, options *commonOptions) ([]*gabs.Container, error) { +func listEntitiesWithOptions(entityType string, options *commonOptions) ([]*gabs.Container, *paging, error) { params := url.Values{} if len(options.Args) > 0 { params.Add("filter", options.Args[0]) } + return listEntitiesOfType(entityType, params, options.OutputJSONResponse, options.Out) } -func filterEntitiesOfType(entityType string, filter string, logJSON bool, out io.Writer) ([]*gabs.Container, error) { +func filterEntitiesOfType(entityType string, filter string, logJSON bool, out io.Writer) ([]*gabs.Container, *paging, error) { params := url.Values{} params.Add("filter", filter) return listEntitiesOfType(entityType, params, logJSON, out) } // listEntitiesOfType queries the Ziti Controller for entities of the given type -func listEntitiesOfType(entityType string, params url.Values, logJSON bool, out io.Writer) ([]*gabs.Container, error) { +func listEntitiesOfType(entityType string, params url.Values, logJSON bool, out io.Writer) ([]*gabs.Container, *paging, error) { jsonParsed, err := util.EdgeControllerList(entityType, params, logJSON, out) if err != nil { - return nil, err + return nil, nil, err } - return jsonParsed.S("data").Children() + children, err := jsonParsed.S("data").Children() + return children, getPaging(jsonParsed), err +} + +func toInt64(c *gabs.Container, path string, errorHolder errorz.ErrorHolder) int64 { + data := c.S(path).Data() + if data == nil { + errorHolder.SetError(errors.Errorf("%v not found", path)) + return 0 + } + val, ok := data.(float64) + if !ok { + errorHolder.SetError(errors.Errorf("%v not a number, it's a %v", path, reflect.TypeOf(data))) + return 0 + } + return int64(val) +} + +func getPaging(c *gabs.Container) *paging { + pagingInfo := &paging{} + pagination := c.S("meta", "pagination") + if pagination != nil { + pagingInfo.limit = toInt64(pagination, "limit", pagingInfo) + pagingInfo.offset = toInt64(pagination, "offset", pagingInfo) + pagingInfo.count = toInt64(pagination, "totalCount", pagingInfo) + } else { + pagingInfo.SetError(errors.New("meta.pagination section not found in result")) + } + return pagingInfo } // listEntitiesOfType queries the Ziti Controller for entities of the given type -func filterSubEntitiesOfType(entityType, subType, entityId, filter string, o *commonOptions) ([]*gabs.Container, error) { +func filterSubEntitiesOfType(entityType, subType, entityId, filter string, o *commonOptions) ([]*gabs.Container, *paging, error) { jsonParsed, err := util.EdgeControllerListSubEntities(entityType, subType, entityId, filter, o.OutputJSONResponse, o.Out) if err != nil { - return nil, err + return nil, nil, err } children, err := jsonParsed.S("data").Children() if err == gabs.ErrNotObjOrArray { - return nil, nil + return nil, getPaging(jsonParsed), nil } - return children, err + return children, getPaging(jsonParsed), err } func runListEdgeRouters(roleFilters []string, roleSemantic string, options *commonOptions) error { @@ -319,15 +373,15 @@ func runListEdgeRouters(roleFilters []string, roleSemantic string, options *comm if roleSemantic != "" { params.Add("roleSemantic", roleSemantic) } - children, err := listEntitiesOfType("edge-routers", params, options.OutputJSONResponse, options.Out) + children, paging, err := listEntitiesOfType("edge-routers", params, options.OutputJSONResponse, options.Out) if err != nil { return err } - return outputEdgeRouters(options, children) + return outputEdgeRouters(options, children, paging) } -func outputEdgeRouters(o *commonOptions, children []*gabs.Container) error { +func outputEdgeRouters(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -340,18 +394,19 @@ func outputEdgeRouters(o *commonOptions, children []*gabs.Container) error { return err } } + pagingInfo.output(o) return nil } func runListEdgeRouterPolicies(o *commonOptions) error { - children, err := listEntitiesWithOptions("edge-router-policies", o) + children, paging, err := listEntitiesWithOptions("edge-router-policies", o) if err != nil { return err } - return outputEdgeRouterPolicies(o, children) + return outputEdgeRouterPolicies(o, children, paging) } -func outputEdgeRouterPolicies(o *commonOptions, children []*gabs.Container) error { +func outputEdgeRouterPolicies(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -366,18 +421,19 @@ func outputEdgeRouterPolicies(o *commonOptions, children []*gabs.Container) erro return err } } + pagingInfo.output(o) return nil } func runListTerminators(o *commonOptions) error { - children, err := listEntitiesWithOptions("terminators", o) + children, pagingInfo, err := listEntitiesWithOptions("terminators", o) if err != nil { return err } - return outputTerminators(o, children) + return outputTerminators(o, children, pagingInfo) } -func outputTerminators(o *commonOptions, children []*gabs.Container) error { +func outputTerminators(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -394,6 +450,7 @@ func outputTerminators(o *commonOptions, children []*gabs.Container) error { return err } } + pagingInfo.output(o) return nil } @@ -414,14 +471,14 @@ func runListServices(asIdentity string, configTypes string, roleFilters []string if roleSemantic != "" { params.Add("roleSemantic", roleSemantic) } - children, err := listEntitiesOfType("services", params, options.OutputJSONResponse, options.Out) + children, pagingInfo, err := listEntitiesOfType("services", params, options.OutputJSONResponse, options.Out) if err != nil { return err } - return outputServices(options, children) + return outputServices(options, children, pagingInfo) } -func outputServices(o *commonOptions, children []*gabs.Container) error { +func outputServices(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -436,19 +493,19 @@ func outputServices(o *commonOptions, children []*gabs.Container) error { return err } } - + pagingInfo.output(o) return nil } func runListServiceEdgeRouterPolices(o *commonOptions) error { - children, err := listEntitiesWithOptions("service-edge-router-policies", o) + children, pagingInfo, err := listEntitiesWithOptions("service-edge-router-policies", o) if err != nil { return err } - return outputServiceEdgeRouterPolicies(o, children) + return outputServiceEdgeRouterPolicies(o, children, pagingInfo) } -func outputServiceEdgeRouterPolicies(o *commonOptions, children []*gabs.Container) error { +func outputServiceEdgeRouterPolicies(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -463,18 +520,19 @@ func outputServiceEdgeRouterPolicies(o *commonOptions, children []*gabs.Containe return err } } + pagingInfo.output(o) return nil } func runListServicePolices(o *commonOptions) error { - children, err := listEntitiesWithOptions("service-policies", o) + children, pagingInfo, err := listEntitiesWithOptions("service-policies", o) if err != nil { return err } - return outputServicePolicies(o, children) + return outputServicePolicies(o, children, pagingInfo) } -func outputServicePolicies(o *commonOptions, children []*gabs.Container) error { +func outputServicePolicies(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -490,6 +548,7 @@ func outputServicePolicies(o *commonOptions, children []*gabs.Container) error { return err } } + pagingInfo.output(o) return nil } @@ -505,15 +564,15 @@ func runListIdentities(roleFilters []string, roleSemantic string, options *commo if roleSemantic != "" { params.Add("roleSemantic", roleSemantic) } - children, err := listEntitiesOfType("identities", params, options.OutputJSONResponse, options.Out) + children, pagingInfo, err := listEntitiesOfType("identities", params, options.OutputJSONResponse, options.Out) if err != nil { return err } - return outputIdentities(options, children) + return outputIdentities(options, children, pagingInfo) } // outputIdentities implements the command to list identities -func outputIdentities(o *commonOptions, children []*gabs.Container) error { +func outputIdentities(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -527,12 +586,13 @@ func outputIdentities(o *commonOptions, children []*gabs.Container) error { return err } } + pagingInfo.output(o) return nil } func runListCAs(o *commonOptions) error { - children, err := listEntitiesWithOptions("cas", o) + children, pagingInfo, err := listEntitiesWithOptions("cas", o) if err != nil { return err } @@ -549,12 +609,12 @@ func runListCAs(o *commonOptions) error { return err } } - + pagingInfo.output(o) return nil } func runListConfigTypes(o *commonOptions) error { - children, err := listEntitiesWithOptions("config-types", o) + children, pagingInfo, err := listEntitiesWithOptions("config-types", o) if err != nil { return err } @@ -570,20 +630,19 @@ func runListConfigTypes(o *commonOptions) error { return err } } - + pagingInfo.output(o) return nil } func runListConfigs(o *commonOptions) error { - children, err := listEntitiesWithOptions("configs", o) + children, pagingInfo, err := listEntitiesWithOptions("configs", o) if err != nil { return err } - return outputConfigs(o, children) + return outputConfigs(o, children, pagingInfo) } -func outputConfigs(o *commonOptions, children []*gabs.Container) error { - +func outputConfigs(o *commonOptions, children []*gabs.Container, pagingInfo *paging) error { if o.OutputJSONResponse { return nil } @@ -601,12 +660,12 @@ func outputConfigs(o *commonOptions, children []*gabs.Container) error { return err } } - + pagingInfo.output(o) return nil } func runListApiSessions(o *commonOptions) error { - children, err := listEntitiesWithOptions("api-sessions", o) + children, pagingInfo, err := listEntitiesWithOptions("api-sessions", o) if err != nil { return err } @@ -623,12 +682,12 @@ func runListApiSessions(o *commonOptions) error { return err } } - + pagingInfo.output(o) return err } func runListSessions(o *commonOptions) error { - children, err := listEntitiesWithOptions("sessions", o) + children, pagingInfo, err := listEntitiesWithOptions("sessions", o) if err != nil { return err @@ -647,12 +706,12 @@ func runListSessions(o *commonOptions) error { return err } } - + pagingInfo.output(o) return err } func runListTransitRouters(o *commonOptions) error { - children, err := listEntitiesWithOptions("transit-routers", o) + children, pagingInfo, err := listEntitiesWithOptions("transit-routers", o) if err != nil { return err @@ -669,7 +728,7 @@ func runListTransitRouters(o *commonOptions) error { return err } } - + pagingInfo.output(o) return err } @@ -686,7 +745,7 @@ func runListServiceRoleAttributes(o *commonOptions) error { } func runListRoleAttributes(entityType string, o *commonOptions) error { - children, err := listEntitiesWithOptions(entityType+"-role-attributes", o) + children, pagingInfo, err := listEntitiesWithOptions(entityType+"-role-attributes", o) if err != nil { return err @@ -701,7 +760,7 @@ func runListRoleAttributes(entityType string, o *commonOptions) error { return err } } - + pagingInfo.output(o) return err } @@ -717,7 +776,7 @@ func runListChilden(parentType, childType string, o *commonOptions, outputF outp filter = o.Args[1] } - children, err := filterSubEntitiesOfType(parentType, childType, parentId, filter, o) + children, pagingInfo, err := filterSubEntitiesOfType(parentType, childType, parentId, filter, o) if err != nil { return err } @@ -726,5 +785,5 @@ func runListChilden(parentType, childType string, o *commonOptions, outputF outp return nil } - return outputF(o, children) + return outputF(o, children, pagingInfo) } diff --git a/ziti/cmd/ziti/cmd/edge_controller/policy_advisor.go b/ziti/cmd/ziti/cmd/edge_controller/policy_advisor.go index 9975ced45..9d29d0192 100644 --- a/ziti/cmd/ziti/cmd/edge_controller/policy_advisor.go +++ b/ziti/cmd/ziti/cmd/edge_controller/policy_advisor.go @@ -206,7 +206,7 @@ func runPolicyAdvisorForIdentities(o *policyAdvisorOptions) error { done := false for !done { filter := fmt.Sprintf(`true skip %v limit 2`, skip) - children, err := filterEntitiesOfType("identities", filter, false, o.Out) + children, _, err := filterEntitiesOfType("identities", filter, false, o.Out) if err != nil { panic(err) } @@ -236,7 +236,7 @@ func runPolicyAdvisorForServices(o *policyAdvisorOptions) error { done := false for !done { filter := fmt.Sprintf(`true skip %v limit 2`, skip) - children, err := filterEntitiesOfType("services", filter, false, o.Out) + children, _, err := filterEntitiesOfType("services", filter, false, o.Out) if err != nil { panic(err) } @@ -267,7 +267,7 @@ func runPolicyAdvisorForIdentity(identityId string, o *policyAdvisorOptions) err for !done { filter := "true limit 2" filter = fmt.Sprintf(`true skip %v limit 2`, skip) - children, err := filterSubEntitiesOfType("identities", "services", identityId, filter, &o.commonOptions) + children, _, err := filterSubEntitiesOfType("identities", "services", identityId, filter, &o.commonOptions) if err != nil { panic(err) } @@ -302,7 +302,7 @@ func runPolicyAdvisorForService(serviceId string, o *policyAdvisorOptions) error for !done { filter := "true limit 2" filter = fmt.Sprintf(`true skip %v limit 2`, skip) - children, err := filterSubEntitiesOfType("services", "identities", serviceId, filter, &o.commonOptions) + children, _, err := filterSubEntitiesOfType("services", "identities", serviceId, filter, &o.commonOptions) if err != nil { panic(err) }