Skip to content
This repository has been archived by the owner on May 6, 2022. It is now read-only.

Commit

Permalink
Make svcat provision support namespaced resources (#2618)
Browse files Browse the repository at this point in the history
- add unit tests for provision_cmd.go
- refactor provision_cmd.go to support new tests
- fix typo ProvisonCmd -> ProvisionCmd
  • Loading branch information
jberkhahn authored and k8s-ci-robot committed May 10, 2019
1 parent ea623e5 commit 82ded7c
Show file tree
Hide file tree
Showing 18 changed files with 2,438 additions and 2,376 deletions.
29 changes: 29 additions & 0 deletions cmd/svcat/instance/instance_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
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 instance_test

import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"

"testing"
)

func TestInstance(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Instance Suite")
}
124 changes: 86 additions & 38 deletions cmd/svcat/instance/provision_cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package instance

import (
"fmt"
"strings"

"github.com/kubernetes-incubator/service-catalog/cmd/svcat/command"
"github.com/kubernetes-incubator/service-catalog/cmd/svcat/output"
Expand All @@ -26,24 +27,29 @@ import (
"github.com/spf13/cobra"
)

type provisonCmd struct {
// ProvisionCmd contains the info needed to provision a new service instance
type ProvisionCmd struct {
*command.Namespaced
*command.Waitable

instanceName string
externalID string
className string
planName string
rawParams []string
jsonParams string
params interface{}
rawSecrets []string
secrets map[string]string
ClassKubeName string
ClassName string
ExternalID string
InstanceName string
JSONParams string
LookupByKubeName bool
Params interface{}
PlanKubeName string
PlanName string
ProvisionClusterInstance bool
RawParams []string
RawSecrets []string
Secrets map[string]string
}

// NewProvisionCmd builds a "svcat provision" command
func NewProvisionCmd(cxt *command.Context) *cobra.Command {
provisionCmd := &provisonCmd{
provisionCmd := &ProvisionCmd{
Namespaced: command.NewNamespaced(cxt),
Waitable: command.NewWaitable(),
}
Expand All @@ -68,70 +74,112 @@ func NewProvisionCmd(cxt *command.Context) *cobra.Command {
PreRunE: command.PreRunE(provisionCmd),
RunE: command.RunE(provisionCmd),
}
provisionCmd.AddNamespaceFlags(cmd.Flags(), false)
cmd.Flags().StringVar(&provisionCmd.externalID, "external-id", "",
"The ID of the instance for use with the OSB SB API (Optional)")
cmd.Flags().StringVar(&provisionCmd.className, "class", "",
"The class name (Required)")
cmd.Flags().StringVar(&provisionCmd.ClassName, "class", "", "The class name (Required)")
cmd.MarkFlagRequired("class")
cmd.Flags().StringVar(&provisionCmd.planName, "plan", "",
"The plan name (Required)")
cmd.Flags().StringVar(&provisionCmd.PlanName, "plan", "", "The plan name (Required)")
cmd.MarkFlagRequired("plan")
cmd.Flags().StringSliceVarP(&provisionCmd.rawParams, "param", "p", nil,
"Additional parameter to use when provisioning the service, format: NAME=VALUE. Cannot be combined with --params-json, Sensitive information should be placed in a secret and specified with --secret")
cmd.Flags().StringSliceVarP(&provisionCmd.rawSecrets, "secret", "s", nil,
"Additional parameter, whose value is stored in a secret, to use when provisioning the service, format: SECRET[KEY]")
cmd.Flags().StringVar(&provisionCmd.jsonParams, "params-json", "",
"Additional parameters to use when provisioning the service, provided as a JSON object. Cannot be combined with --param")
cmd.Flags().StringVar(&provisionCmd.ExternalID, "external-id", "", "The ID of the instance for use with the OSB SB API (Optional)")
cmd.Flags().BoolVarP(&provisionCmd.LookupByKubeName, "kube-name", "k", false, "Whether or not to interpret the Class/Plan names as Kubernetes names (the default is by external name)")
cmd.Flags().StringSliceVarP(&provisionCmd.RawParams, "param", "p", nil, "Additional parameter to use when provisioning the service, format: NAME=VALUE. Cannot be combined with --params-json, Sensitive information should be placed in a secret and specified with --secret")
cmd.Flags().StringVar(&provisionCmd.JSONParams, "params-json", "", "Additional parameters to use when provisioning the service, provided as a JSON object. Cannot be combined with --param")
cmd.Flags().StringSliceVarP(&provisionCmd.RawSecrets, "secret", "s", nil, "Additional parameter, whose value is stored in a secret, to use when provisioning the service, format: SECRET[KEY]")
provisionCmd.AddNamespaceFlags(cmd.Flags(), false)
provisionCmd.AddWaitFlags(cmd)

return cmd
}

func (c *provisonCmd) Validate(args []string) error {
// Validate ensures the required args were provided
// and parses provided params and secrets
func (c *ProvisionCmd) Validate(args []string) error {
if len(args) == 0 {
return fmt.Errorf("an instance name is required")
}
c.instanceName = args[0]
c.InstanceName = args[0]

var err error

if c.jsonParams != "" && len(c.rawParams) > 0 {
if c.JSONParams != "" && len(c.RawParams) > 0 {
return fmt.Errorf("--params-json cannot be used with --param")
}

if c.jsonParams != "" {
c.params, err = parameters.ParseVariableJSON(c.jsonParams)
if c.JSONParams != "" {
c.Params, err = parameters.ParseVariableJSON(c.JSONParams)
if err != nil {
return fmt.Errorf("invalid --params-json value (%s)", err)
}
} else {
c.params, err = parameters.ParseVariableAssignments(c.rawParams)
c.Params, err = parameters.ParseVariableAssignments(c.RawParams)
if err != nil {
return fmt.Errorf("invalid --param value (%s)", err)
}
}

c.secrets, err = parameters.ParseKeyMaps(c.rawSecrets)
c.Secrets, err = parameters.ParseKeyMaps(c.RawSecrets)
if err != nil {
return fmt.Errorf("invalid --secret value (%s)", err)
}

return nil
}

func (c *provisonCmd) Run() error {
return c.Provision()
// Run calls the Provision method
func (c *ProvisionCmd) Run() error {
err := c.findKubeNames()
if err != nil {
return err
}
return c.provision()
}

// FindKubeNames determines if we need to find the Kubernetes
// metadata names of the Class/Plan, and finds them if we do.
// It also sets whether we are provisioning a ClusterServiceClass
// or ServiceClass instance
func (c *ProvisionCmd) findKubeNames() error {
scopeOpts := servicecatalog.ScopeOptions{
Namespace: c.Namespace,
Scope: servicecatalog.AllScope,
}
if c.LookupByKubeName {
c.ClassKubeName = c.ClassName
c.PlanKubeName = c.PlanName

class, err := c.App.RetrieveClassByID(c.ClassKubeName, scopeOpts)
if err != nil {
return err
}
c.ProvisionClusterInstance = class.IsClusterServiceClass()
return nil
} // else lookup by external name
class, err := c.App.RetrieveClassByName(c.ClassName, scopeOpts)
if err != nil {
if strings.Contains(err.Error(), "more than one matching class") {
return fmt.Errorf("More than one class '%s' found, please specify Kubernetes names using --kube-name", c.ClassName)
}
return err
}
c.ClassKubeName = class.GetName()
c.ProvisionClusterInstance = class.IsClusterServiceClass()
plan, err := c.App.RetrievePlanByClassIDAndName(c.ClassKubeName, c.PlanName, scopeOpts)
if err != nil {
return fmt.Errorf("Unable to find plan '%s': %s", c.PlanName, err.Error())
}
c.PlanKubeName = plan.GetName()
return nil
}

func (c *provisonCmd) Provision() error {
// Provision calls the pkg/svcat lib to provision the instance,
// waits if necessary, and then displays the created instance
// to the user
func (c *ProvisionCmd) provision() error {
opts := &servicecatalog.ProvisionOptions{
ExternalID: c.externalID,
ExternalID: c.ExternalID,
Namespace: c.Namespace,
Params: c.params,
Secrets: c.secrets,
Params: c.Params,
Secrets: c.Secrets,
}
instance, err := c.App.Provision(c.instanceName, c.className, c.planName, opts)
instance, err := c.App.Provision(c.InstanceName, c.ClassKubeName, c.PlanKubeName, c.ProvisionClusterInstance, opts)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 82ded7c

Please sign in to comment.