Skip to content

Commit

Permalink
[svcat] Adding a filter to get plan. (openshift#1758)
Browse files Browse the repository at this point in the history
* Adding a filter idea to get plan. Looking for feedback.

* Reworked based on feedback. Class is a bool flag.

* use caps for help.

* Fix the unit test.

* Add unit test for new plans for class case.

* Kibbles convinced me to turn back --class into a string arg and make it an alt way to do class/plan get.

* Adding another class to UPS. Tests filtering now.

* Add doc on FilterOptions.
  • Loading branch information
n3wscott authored and kibbles-n-bytes committed Mar 15, 2018
1 parent 70afb56 commit cc02f0e
Show file tree
Hide file tree
Showing 11 changed files with 218 additions and 24 deletions.
79 changes: 64 additions & 15 deletions cmd/svcat/plan/get_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/kubernetes-incubator/service-catalog/cmd/svcat/command"
"github.com/kubernetes-incubator/service-catalog/cmd/svcat/output"
"github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1"
"github.com/kubernetes-incubator/service-catalog/pkg/svcat/service-catalog"
"github.com/spf13/cobra"
)

Expand All @@ -31,6 +32,10 @@ type getCmd struct {
lookupByUUID bool
uuid string
name string

classFilter string
classUUID string
className string
}

// NewGetCmd builds a "svcat get plans" command
Expand All @@ -39,11 +44,16 @@ func NewGetCmd(cxt *command.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "plans [name]",
Aliases: []string{"plan", "pl"},
Short: "List plans, optionally filtered by name",
Short: "List plans, optionally filtered by name or class",
Example: `
svcat get plans
svcat get plan standard800
svcat get plan --uuid 08e4b43a-36bc-447e-a81f-8202b13e339c
svcat get plan PLAN_NAME
svcat get plan CLASS_NAME/PLAN_NAME
svcat get plan --uuid PLAN_UUID
svcat get plans --class CLASS_NAME
svcat get plan --class CLASS_NAME PLAN_NAME
svcat get plans --uuid --class CLASS_UUID
svcat get plan --uuid --class CLASS_UUID PLAN_UUID
`,
PreRunE: command.PreRunE(getCmd),
RunE: command.RunE(getCmd),
Expand All @@ -55,17 +65,38 @@ func NewGetCmd(cxt *command.Context) *cobra.Command {
false,
"Whether or not to get the plan by UUID (the default is by name)",
)
cmd.Flags().StringVarP(
&getCmd.classFilter,
"class",
"c",
"",
"Filter plans based on class. When --uuid is specified, the class name is interpreted as a uuid.",
)
return cmd
}

func (c *getCmd) Validate(args []string) error {
if len(args) > 0 {
if c.lookupByUUID {
c.uuid = args[0]
} else if strings.Contains(args[0], "/") {
names := strings.Split(args[0], "/")
if len(names) != 2 {
return fmt.Errorf("failed to parse class/plan name combination '%s'", c.name)
}
c.className = names[0]
c.name = names[1]
} else {
c.name = args[0]
}
}
if c.classFilter != "" {
if c.lookupByUUID {
c.classUUID = c.classFilter
} else {
c.className = c.classFilter
}
}

return nil
}
Expand All @@ -79,34 +110,52 @@ func (c *getCmd) Run() error {
}

func (c *getCmd) getAll() error {
plans, err := c.App.RetrievePlans()
if err != nil {
return fmt.Errorf("unable to list plans (%s)", err)
}

var opts *servicecatalog.FilterOptions

// Retrieve the classes as well because plans don't have the external class name
classes, err := c.App.RetrieveClasses()
if err != nil {
return fmt.Errorf("unable to list classes (%s)", err)
}

if c.classFilter != "" {
if !c.lookupByUUID {
// Map the external class name to the class name.
for _, class := range classes {
if c.className == class.Spec.ExternalName {
c.classUUID = class.Name
break
}
}
}
opts = &servicecatalog.FilterOptions{
ClassID: c.classUUID,
}
}

plans, err := c.App.RetrievePlans(opts)
if err != nil {
return fmt.Errorf("unable to list plans (%s)", err)
}

output.WritePlanList(c.Output, plans, classes)
return nil
}

func (c *getCmd) get() error {
var plan *v1beta1.ClusterServicePlan
var err error
if c.lookupByUUID {
switch {
case c.lookupByUUID:
plan, err = c.App.RetrievePlanByID(c.uuid)
} else if strings.Contains(c.name, "/") {
names := strings.Split(c.name, "/")
if len(names) != 2 {
return fmt.Errorf("failed to parse class/plan name combination '%s'", c.name)
}
plan, err = c.App.RetrievePlanByClassAndPlanNames(names[0], names[1])
} else {

case c.className != "":
plan, err = c.App.RetrievePlanByClassAndPlanNames(c.className, c.name)

default:
plan, err = c.App.RetrievePlanByName(c.name)

}
if err != nil {
return err
Expand Down
4 changes: 4 additions & 0 deletions cmd/svcat/svcat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ func TestCommandOutput(t *testing.T) {
{name: "get plan by name", cmd: "get plan default", golden: "output/get-plan.txt"},
{name: "get plan by uuid", cmd: "get plan --uuid 86064792-7ea2-467b-af93-ac9694d96d52", golden: "output/get-plan.txt"},
{name: "get plan by class/plan name combo", cmd: "get plan user-provided-service/default", golden: "output/get-plan.txt"},
{name: "get plan by class name", cmd: "get plan --class user-provided-service", golden: "output/get-plans-by-class.txt"},
{name: "get plan by class/plan name combo", cmd: "get plan --class user-provided-service default", golden: "output/get-plan.txt"},
{name: "get plan by class/plan uuid combo", cmd: "get plan --uuid --class 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468 86064792-7ea2-467b-af93-ac9694d96d52", golden: "output/get-plan.txt"},
{name: "get plan by class uuid", cmd: "get plan --uuid --class 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468", golden: "output/get-plans-by-class.txt"},
{name: "describe plan by name", cmd: "describe plan default", golden: "output/describe-plan.txt"},
{name: "describe plan by uuid", cmd: "describe plan --uuid 86064792-7ea2-467b-af93-ac9694d96d52", golden: "output/describe-plan.txt"},
{name: "describe plan by class/plan name combo", cmd: "describe plan user-provided-service/default", golden: "output/describe-plan.txt"},
Expand Down
7 changes: 4 additions & 3 deletions cmd/svcat/testdata/output/get-classes.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
NAME DESCRIPTION UUID
+-----------------------+-------------------------+--------------------------------------+
user-provided-service A user provided service 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468
NAME DESCRIPTION UUID
+--------------------------+--------------------------+--------------------------------------+
user-provided-service A user provided service 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468
another-provided-service Another provided service f1a80068-e366-494e-92d6-a0782337945b
4 changes: 4 additions & 0 deletions cmd/svcat/testdata/output/get-plans-by-class.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
NAME CLASS DESCRIPTION UUID
+---------+-----------------------+-------------------------+--------------------------------------+
default user-provided-service Sample plan description 86064792-7ea2-467b-af93-ac9694d96d52
premium user-provided-service Premium plan cc0d7529-18e8-416d-8946-6f7456acd589
11 changes: 7 additions & 4 deletions cmd/svcat/testdata/output/get-plans.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
NAME CLASS DESCRIPTION UUID
+---------+-----------------------+-------------------------+--------------------------------------+
default user-provided-service Sample plan description 86064792-7ea2-467b-af93-ac9694d96d52
premium user-provided-service Premium plan cc0d7529-18e8-416d-8946-6f7456acd589
NAME CLASS DESCRIPTION UUID
+---------+--------------------------+--------------------------------+--------------------------------------+
default user-provided-service Sample plan description 86064792-7ea2-467b-af93-ac9694d96d52
premium user-provided-service Premium plan cc0d7529-18e8-416d-8946-6f7456acd589
default another-provided-service Another sample plan 25b9b299-b0b3-4e14-aa1a-242eeb788aca
description
premium another-provided-service Another premium plan c1dbdafe-f987-4d36-8c9b-2aaaff740d4a
6 changes: 5 additions & 1 deletion cmd/svcat/testdata/plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,13 @@ tree:
- name: all-namespaces
desc: List all instances across namespaces
- name: plans
shortDesc: List plans, optionally filtered by name
shortDesc: List plans, optionally filtered by name or class
command: ./svcat get plans
flags:
- name: class
shorthand: c
desc: Filter plans based on class. When --uuid is specified, the class name
is interpreted as a uuid.
- name: uuid
shorthand: u
desc: Whether or not to get the plan by UUID (the default is by name)
Expand Down
21 changes: 21 additions & 0 deletions cmd/svcat/testdata/responses/clusterserviceclasses.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,27 @@
"status": {
"removedFromBrokerCatalog": false
}
},
{
"metadata": {
"name": "f1a80068-e366-494e-92d6-a0782337945b",
"selfLink": "/apis/servicecatalog.k8s.io/v1beta1/clusterserviceclasses/f1a80068-e366-494e-92d6-a0782337945b",
"uid": "5be743ff-06bc-4d49-b762-c8b1470916c4",
"resourceVersion": "6",
"creationTimestamp": "2018-02-26T20:53:31Z"
},
"spec": {
"clusterServiceBrokerName": "ups-broker",
"externalName": "another-provided-service",
"externalID": "f1a80068-e366-494e-92d6-a0782337945b",
"description": "Another provided service",
"bindable": true,
"bindingRetrievable": false,
"planUpdatable": true
},
"status": {
"removedFromBrokerCatalog": false
}
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"kind": "ClusterServiceClass",
"apiVersion": "servicecatalog.k8s.io/v1beta1",
"metadata": {
"name": "f1a80068-e366-494e-92d6-a0782337945b",
"selfLink": "/apis/servicecatalog.k8s.io/v1beta1/clusterserviceclasses/f1a80068-e366-494e-92d6-a0782337945b",
"uid": "7b3c2fe0-f711-11e7-aa44-0242ac110005",
"resourceVersion": "3",
"creationTimestamp": "2018-01-11T20:53:31Z"
},
"spec": {
"clusterServiceBrokerName": "ups-broker",
"externalName": "another-provided-service",
"externalID": "5be743ff-06bc-4d49-b762-c8b1470916c4",
"description": "Another provided service",
"bindable": true,
"bindingRetrievable": false,
"planUpdatable": true
},
"status": {
"removedFromBrokerCatalog": false
}
}
53 changes: 53 additions & 0 deletions cmd/svcat/testdata/responses/clusterserviceplans.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,59 @@
"status": {
"removedFromBrokerCatalog": false
}
},
{
"metadata": {
"name": "25b9b299-b0b3-4e14-aa1a-242eeb788aca",
"selfLink": "/apis/servicecatalog.k8s.io/v1beta1/clusterserviceplans/25b9b299-b0b3-4e14-aa1a-242eeb788aca",
"uid": "7b3d0190-f711-11e7-aa44-0242ac110005",
"resourceVersion": "4",
"creationTimestamp": "2018-01-11T20:53:31Z"
},
"spec": {
"clusterServiceBrokerName": "ups-broker",
"externalName": "default",
"externalID": "090b5eac-dfa4-49f3-827d-8bcaf3a5bd7c",
"description": "Another sample plan description",
"free": true,
"clusterServiceClassRef": {
"name": "f1a80068-e366-494e-92d6-a0782337945b"
}
},
"status": {
"removedFromBrokerCatalog": false
}
},
{
"metadata": {
"name": "c1dbdafe-f987-4d36-8c9b-2aaaff740d4a",
"selfLink": "/apis/servicecatalog.k8s.io/v1beta1/clusterserviceplans/c1dbdafe-f987-4d36-8c9b-2aaaff740d4a",
"uid": "357feef4-0445-4a4c-a3bf-99762f2d36a2",
"resourceVersion": "5",
"creationTimestamp": "2018-01-11T20:53:31Z"
},
"spec": {
"clusterServiceBrokerName": "ups-broker",
"externalName": "premium",
"externalID": "adf134dc-0b0d-4c74-a6da-6ee1a5e34b8a",
"description": "Another premium plan",
"free": false,
"clusterServiceClassRef": {
"name": "f1a80068-e366-494e-92d6-a0782337945b"
},
"instanceCreateParameterSchema": {
"properties": {
"testInstanceProperty": {
"description": "Another test instance property.",
"type": "string"
}
},
"required": [
"testInstanceProperty"
],
"type": "object"
}
}
}
]
}
22 changes: 22 additions & 0 deletions pkg/svcat/service-catalog/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package servicecatalog

// FilterOptions allows for optional filtering fields to be passed to `Retrieve` methods.
type FilterOptions struct {
ClassID string
}
12 changes: 11 additions & 1 deletion pkg/svcat/service-catalog/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,22 @@ const (
)

// RetrievePlans lists all plans defined in the cluster.
func (sdk *SDK) RetrievePlans() ([]v1beta1.ClusterServicePlan, error) {
func (sdk *SDK) RetrievePlans(opts *FilterOptions) ([]v1beta1.ClusterServicePlan, error) {
plans, err := sdk.ServiceCatalog().ClusterServicePlans().List(v1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("unable to list plans (%s)", err)
}

if opts != nil && opts.ClassID != "" {
plansFiltered := make([]v1beta1.ClusterServicePlan, 0)
for _, p := range plans.Items {
if p.Spec.ClusterServiceClassRef.Name == opts.ClassID {
plansFiltered = append(plansFiltered, p)
}
}
return plansFiltered, nil
}

return plans.Items, nil
}

Expand Down

0 comments on commit cc02f0e

Please sign in to comment.