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

Add svcat command to create user-defined cluster-scoped classes #2190

Merged
merged 16 commits into from
Jul 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 81 additions & 0 deletions cmd/svcat/class/create_cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
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 class

import (
"fmt"

"github.com/kubernetes-incubator/service-catalog/cmd/svcat/command"
"github.com/kubernetes-incubator/service-catalog/cmd/svcat/output"
"github.com/spf13/cobra"
)

// CreateCmd contains the information needed to create a new class
type CreateCmd struct {
*command.Context
Name string
From string
}

// NewCreateCmd builds a "svcat create class" command
func NewCreateCmd(cxt *command.Context) *cobra.Command {
createCmd := &CreateCmd{Context: cxt}
cmd := &cobra.Command{
Use: "class [NAME] --from [EXISTING_NAME]",
Short: "Copies an existing class into a new user-defined cluster-scoped class",
Example: command.NormalizeExamples(`
svcat create class newclass --from mysqldb
`),
PreRunE: command.PreRunE(createCmd),
RunE: command.RunE(createCmd),
}
cmd.Flags().StringVarP(&createCmd.From, "from", "f", "",
"Name from an existing class that will be copied (Required)",
)
cmd.MarkFlagRequired("from")

return cmd
}

// Validate checks that the required arguments have been provided
func (c *CreateCmd) Validate(args []string) error {
if len(args) <= 0 {
return fmt.Errorf("new class name should be provided")
}

c.Name = args[0]

return nil
}

// Run calls out to the pkg lib to create the class and displays the output
func (c *CreateCmd) Run() error {
class, err := c.App.RetrieveClassByName(c.From)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, there will need to be a follow-on PR to make this work for namespaced classes. That feature is super new so it doesn't need to be in this PR. Just giving you a heads up in case you want to pick up more related stuff after this PR is merged! 😀

if err != nil {
return err
}

class.Name = c.Name

createdClass, err := c.App.CreateClass(class)
if err != nil {
return err
}

output.WriteClassDetails(c.Output, createdClass)
return nil
}
99 changes: 99 additions & 0 deletions cmd/svcat/class/create_cmd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
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 class_test

import (
"bytes"

. "github.com/kubernetes-incubator/service-catalog/cmd/svcat/class"
"github.com/kubernetes-incubator/service-catalog/cmd/svcat/command"
"github.com/kubernetes-incubator/service-catalog/cmd/svcat/test"
"github.com/kubernetes-incubator/service-catalog/pkg/apis/servicecatalog/v1beta1"
"github.com/kubernetes-incubator/service-catalog/pkg/svcat"
servicecatalogfakes "github.com/kubernetes-incubator/service-catalog/pkg/svcat/service-catalog/service-catalogfakes"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"k8s.io/apimachinery/pkg/apis/meta/v1"
)

var _ = Describe("Create Command", func() {
Describe("NewCreateCmd", func() {
It("Builds and returns a cobra command", func() {
cxt := &command.Context{}
cmd := NewCreateCmd(cxt)
Expect(*cmd).NotTo(BeNil())
Expect(cmd.Use).To(Equal("class [NAME] --from [EXISTING_NAME]"))
Expect(cmd.Short).To(ContainSubstring("Copies an existing class into a new user-defined cluster-scoped class"))
Expect(cmd.Example).To(ContainSubstring("svcat create class newclass --from mysqldb"))
Expect(len(cmd.Aliases)).To(Equal(0))
})
})
Describe("Validate name is provided", func() {
It("errors if a new class name is not provided", func() {
cmd := CreateCmd{
Context: nil,
Name: "",
From: "existingclass",
}
err := cmd.Validate([]string{})
Expect(err).To(HaveOccurred())
})
})
Describe("Validate from is provided", func() {
It("errors if a existing class name is not provided using from", func() {
cmd := CreateCmd{
Context: nil,
Name: "newclass",
From: "",
}
err := cmd.Validate([]string{})
Expect(err).To(HaveOccurred())
})
})
Describe("Create", func() {
It("Calls the pkg/svcat libs Create method with the passed in variables and prints output to the user", func() {
className := "newclass"
existingClassName := "existingclass"

classToReturn := &v1beta1.ClusterServiceClass{
ObjectMeta: v1.ObjectMeta{
Name: className,
},
}

outputBuffer := &bytes.Buffer{}

fakeApp, _ := svcat.NewApp(nil, nil, "default")
fakeSDK := new(servicecatalogfakes.FakeSvcatClient)
fakeSDK.CreateClassReturns(classToReturn, nil)
fakeApp.SvcatClient = fakeSDK
cmd := CreateCmd{
Context: svcattest.NewContext(outputBuffer, fakeApp),
Name: className,
From: existingClassName,
}
err := cmd.Run()

Expect(err).NotTo(HaveOccurred())
class := fakeSDK.CreateClassArgsForCall(0)
Expect(class.Name).To(Equal(className))

output := outputBuffer.String()
Expect(output).To(ContainSubstring(className))
})
})
})
11 changes: 11 additions & 0 deletions cmd/svcat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ func buildRootCommand(cxt *command.Context) *cobra.Command {
cmd.PersistentFlags().StringVar(&opts.KubeContext, "context", "", "name of the kubeconfig context to use.")
cmd.PersistentFlags().StringVar(&opts.KubeConfig, "kubeconfig", "", "path to kubeconfig file. Overrides $KUBECONFIG")

cmd.AddCommand(newCreateCmd(cxt))
cmd.AddCommand(newGetCmd(cxt))
cmd.AddCommand(newDescribeCmd(cxt))
cmd.AddCommand(broker.NewRegisterCmd(cxt))
Expand Down Expand Up @@ -144,6 +145,16 @@ func newSyncCmd(cxt *command.Context) *cobra.Command {
return cmd
}

func newCreateCmd(cxt *command.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Create a user-defined resource",
}
cmd.AddCommand(class.NewCreateCmd(cxt))

return cmd
}

func newGetCmd(cxt *command.Context) *cobra.Command {
cmd := &cobra.Command{
Use: "get",
Expand Down
1 change: 1 addition & 0 deletions cmd/svcat/svcat_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func TestCommandOutput(t *testing.T) {
{name: "get class by uuid", cmd: "get class --uuid 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468", golden: "output/get-class.txt"},
{name: "describe class by name", cmd: "describe class user-provided-service", golden: "output/describe-class.txt"},
{name: "describe class uuid", cmd: "describe class --uuid 4f6e6cf6-ffdd-425f-a2c7-3c9258ad2468", golden: "output/describe-class.txt"},
{name: "create class", cmd: "create class new-class --from user-provided-service", golden: "output/create-class.txt"},

{name: "list all plans", cmd: "get plans", golden: "output/get-plans.txt"},
{name: "list all plans (json)", cmd: "get plans -o json", golden: "output/get-plans.json"},
Expand Down
51 changes: 51 additions & 0 deletions cmd/svcat/testdata/output/completion-bash.txt
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,56 @@ _svcat_completion()
noun_aliases=()
}

_svcat_create_class()
{
last_command="svcat_create_class"
commands=()

flags=()
two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=()
flags_completion=()

flags+=("--from=")
two_word_flags+=("-f")
local_nonpersistent_flags+=("--from=")
flags+=("--context=")
flags+=("--kubeconfig=")
flags+=("--logtostderr")
flags+=("--v=")
two_word_flags+=("-v")

must_have_one_flag=()
must_have_one_flag+=("--from=")
must_have_one_flag+=("-f")
must_have_one_noun=()
noun_aliases=()
}

_svcat_create()
{
last_command="svcat_create"
commands=()
commands+=("class")

flags=()
two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=()
flags_completion=()

flags+=("--context=")
flags+=("--kubeconfig=")
flags+=("--logtostderr")
flags+=("--v=")
two_word_flags+=("-v")

must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}

_svcat_deprovision()
{
last_command="svcat_deprovision"
Expand Down Expand Up @@ -952,6 +1002,7 @@ _svcat_root_command()
commands=()
commands+=("bind")
commands+=("completion")
commands+=("create")
commands+=("deprovision")
commands+=("describe")
commands+=("get")
Expand Down
51 changes: 51 additions & 0 deletions cmd/svcat/testdata/output/completion-zsh.txt
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,56 @@ _svcat_completion()
noun_aliases=()
}

_svcat_create_class()
{
last_command="svcat_create_class"
commands=()

flags=()
two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=()
flags_completion=()

flags+=("--from=")
two_word_flags+=("-f")
local_nonpersistent_flags+=("--from=")
flags+=("--context=")
flags+=("--kubeconfig=")
flags+=("--logtostderr")
flags+=("--v=")
two_word_flags+=("-v")

must_have_one_flag=()
must_have_one_flag+=("--from=")
must_have_one_flag+=("-f")
must_have_one_noun=()
noun_aliases=()
}

_svcat_create()
{
last_command="svcat_create"
commands=()
commands+=("class")

flags=()
two_word_flags=()
local_nonpersistent_flags=()
flags_with_completion=()
flags_completion=()

flags+=("--context=")
flags+=("--kubeconfig=")
flags+=("--logtostderr")
flags+=("--v=")
two_word_flags+=("-v")

must_have_one_flag=()
must_have_one_noun=()
noun_aliases=()
}

_svcat_deprovision()
{
last_command="svcat_deprovision"
Expand Down Expand Up @@ -1086,6 +1136,7 @@ _svcat_root_command()
commands=()
commands+=("bind")
commands+=("completion")
commands+=("create")
commands+=("deprovision")
commands+=("describe")
commands+=("get")
Expand Down
6 changes: 6 additions & 0 deletions cmd/svcat/testdata/output/create-class.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Name: user-provided-service
Description: A user provided service
UUID: new-class
Status: Active
Tags:
Broker: ups-broker
14 changes: 14 additions & 0 deletions cmd/svcat/testdata/plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ tree:
Svcat shell completion\\nsource '$HOME/.svcat/svcat_completion.bash.inc'\\n\"
>> $HOME/.bash_profile\n source $HOME/.bash_profile"
command: ./svcat completion
- name: create
use: create
shortDesc: Create a user-defined resource
command: ./svcat create
tree:
- name: class
use: class [NAME] --from [EXISTING_NAME]
shortDesc: Copies an existing class into a new user-defined cluster-scoped class
example: ' svcat create class newclass --from mysqldb'
command: ./svcat create class
flags:
- name: from
shorthand: f
desc: Name from an existing class that will be copied (Required)
- name: deprovision
use: deprovision NAME
shortDesc: Deletes an instance of a service
Expand Down
13 changes: 13 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ Plans:
premium Premium plan
```

## Copies an exisitng class into a new user-defined class

This copies an exisitng class specified by name into a new user-defined one with new specified name.
```console
$ svcat create class new-class --from user-provided-service
Name: user-provided-service
Description: A user provided service
UUID: new-class
Status: Active
Tags:
Broker: ups-broker
```

## Provision a service

```console
Expand Down
Loading