-
Notifications
You must be signed in to change notification settings - Fork 382
Add svcat command to create user-defined cluster-scoped classes #2190
Add svcat command to create user-defined cluster-scoped classes #2190
Conversation
Woooooooooooooooo! |
/ok-to-test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking great so far!
cmd/svcat/class/create_cmd.go
Outdated
PreRunE: command.PreRunE(createCmd), | ||
RunE: command.RunE(createCmd), | ||
} | ||
cmd.Flags().StringVarP( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: this could be a single line or maybe just this instead
cmd.Flags().StringVarP(&createCmd.from, "from", "f", "",
"Name from an existing class that will be copied")
Just letting you know that splitting it up per line isn't a style that you have to use, in case you copied it from elsewhere in our code.
cmd/svcat/class/create_cmd.go
Outdated
return fmt.Errorf("new class name should be provided") | ||
} | ||
|
||
if c.from == "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have a cobra trick for you! You can do this instead when you define the flag and then cobra will enforce this for you.
cmd.MarkFlagRequired("from")
cmd/svcat/class/create_cmd.go
Outdated
|
||
class.Spec.ExternalName = c.name | ||
|
||
_, err = c.App.CreateClass(class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
@@ -207,4 +207,38 @@ var _ = Describe("Class", func() { | |||
Expect(actions[0].(testing.GetActionImpl).Name).To(Equal(fakeClassName)) | |||
}) | |||
}) | |||
Describe("CreateClassByName", func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if this is the right name for the test, no? The function takes in the entire class resource, not just a name. How about CreateClass
instead?
Describe("CreateClassByName", func() { | ||
It("Calls the generated v1beta1 create method with the passed in class", func() { | ||
realClient := &fake.Clientset{} | ||
realClient.AddReactor("create", "clusterserviceclasses", func(action testing.Action) (bool, runtime.Object, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't believe that you need to define a Create Reactor here? Pretty sure that the default test fake handles this for you already. Does it still work if you remove lines 213-215?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah you don't need to manually add a reactor for the simple case like here. See the test case for provision instance here.
Yo! Neat PR, definitely interested in this. I'm in the middle of refactoring the svcat cmd/pkg interface (as well as adding some cmd/svcat unit tests that you could crib from) here: #2162. Will take a look and comment. |
cmd/svcat/class/create_cmd.go
Outdated
return err | ||
} | ||
|
||
class.Spec.ExternalName = c.name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually work? Wouldn't it barf because you're trying to create a new class with the same UID as the original class? Also, the Status in the new class you're trying to create would be fully inflated, which might cause strange things to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should work. K8s handles generating a new uid, and will ignore status because it's a subresource. Either way we should add some tests to verify that assumption.
@@ -77,3 +77,13 @@ func (sdk *SDK) RetrieveClassByPlan(plan *v1beta1.ClusterServicePlan, | |||
|
|||
return class, nil | |||
} | |||
|
|||
// CreateClass returns new created class | |||
func (sdk *SDK) CreateClass(class *v1beta1.ClusterServiceClass) (*v1beta1.ClusterServiceClass, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we going to come up with a unique verb for creating a class like register/provision/bind for brokers/instances/bindings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't change this one on latest commit. Since I don't have any strong opinion about it, I will wait for a decision :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't planning on it, no.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create class seems fine, then.
@@ -207,4 +207,38 @@ var _ = Describe("Class", func() { | |||
Expect(actions[0].(testing.GetActionImpl).Name).To(Equal(fakeClassName)) | |||
}) | |||
}) | |||
Describe("CreateClassByName", func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ginkgo tests yeah!
Just one caveat, this describe should be "CreateClass" as that's the method you created, and could have another describe inside called "CreateClassByName". Or maybe actually all the describes should be Context(method name) and there should be Describe(test case situation) inside them. Hmmm.
Describe("CreateClassByName", func() { | ||
It("Calls the generated v1beta1 create method with the passed in class", func() { | ||
realClient := &fake.Clientset{} | ||
realClient.AddReactor("create", "clusterserviceclasses", func(action testing.Action) (bool, runtime.Object, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah you don't need to manually add a reactor for the simple case like here. See the test case for provision instance here.
- Add 'create' command to svcat main.go; - Changes Cobra command declaration; - Improve new test on pkg/svcat/service-catalog/class_test.go - Fix data provided to new generated class on cmd/svcat/class/create_cmd.go - Print success message to the user after creating a class - Add unittest to cmd/svcat/svcat_test.go - Update goldenfiles
@carolynvs / @jberkhahn thanks a lot for reviewing this, hopefully latest commit address most of previous comments :) |
cmd/svcat/class/create_cmd.go
Outdated
@@ -73,12 +67,22 @@ func (c *createCmd) Run() error { | |||
return err | |||
} | |||
|
|||
class.Spec.ExternalName = c.name | |||
newClass := &v1beta1.ClusterServiceClass{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm confused about why you changed this part? Previously, it would copy the spec of the original class, and save it with a new name which was the desired behavior. With this change, the new class is missing key information from the original and it wouldn't be usable.
My vote is for going back to what you had and making sure through tests that it does what we need without unwanted side effects (if that was the concern that made you change it?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just realized that this was in response to one of Jonathan's comments above. I replied there but I believe we can go back to what you had before (I spoke with him in person about this last week) and just make sure that we have enough tests to verify that it works as we hoped, without odd side-effects.
cmd/svcat/class/create_cmd.go
Outdated
if err != nil { | ||
return err | ||
} | ||
|
||
output.WriteCreatedResourceName(c.Output, createdClass.Spec.ExternalName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
cmd/svcat/main.go
Outdated
func newCreateCmd(cxt *command.Context) *cobra.Command { | ||
cmd := &cobra.Command{ | ||
Use: "create", | ||
Short: "Create an user-defined resource", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: change "an" to "a"
Expect(actions[0].Matches("create", "clusterserviceclasses")).To(BeTrue()) | ||
objectFromRequest := actions[0].(testing.CreateActionImpl).Object.(*v1beta1.ClusterServiceClass) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question as above, this is a change in behavior and I'm not sure what's driving it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My PR just went in to refactor the cmd/pkg into an interface so it can be faked out. Need to fix this so it works on top of the new code. Also, add a unit test for the cmd layer. Hopefully it shouldn't be too hard to figure out, should be pretty close to the one I added for cmd/svcat/register_broker_cmd.go. You will need to add CreateClass to the interface and regenerate the fakes with (Counterfeiter)[https://github.com/maxbrunsfeld/counterfeiter]
If you need any help, message me on the Kube slack and I can walk you through it.
cmd/svcat/output/output.go
Outdated
@@ -53,6 +53,11 @@ func formatStatusFull(condition string, conditionStatus v1beta1.ConditionStatus, | |||
return fmt.Sprintf("%s - %s @ %s", status, message, timestamp.UTC()) | |||
} | |||
|
|||
// WriteCreatedResourceName prints the name of a created resource | |||
func WriteCreatedResourceName(w io.Writer, resourceName string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we don't have anything suitable for this in output/class.go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand the question/suggestion?
For other resources when we create them (like a binding), we have just printed out the details (basically the output of describe). We haven't had svcat create class resources yet so this is net new. Once we start allowing creating plans too, this function will be resued.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No? We couldn't use something like WriteClass or WriteClassDetails?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh! I didn't get that you are were suggesting that we output WriteClassDetails instead of just the name. 😊 Yeah that would be fine with me either way.
/assign @carolynvs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What you have so far looks good to me, but since it's a new feature it will also need docs.
Can you add a section to https://github.com/kubernetes-incubator/service-catalog/blob/master/docs/cli.md called "Create a custom class" and show an example command and output?
Looks like your golden files are out of date (or there is a bug in one of the tests) based on the failed build. https://svc-cat.io/docs/devguide/#golden-files explains how to regenerate the golden files. |
@artmello Let me know if you need help getting the build passing, I have time this week to help get this PR ready to merge. 😀 |
f709986
to
39e34bf
Compare
- Change Name instead of ExternalName
- Change Name instead of ExternalName - Add CreateClass to sdk interface
f4d3cfb
to
ff4a0b7
Compare
@carolynvs / @jberkhahn thanks a lot for the review so far, hopefully all your comments are addressed with latest commits. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/lgtm
ZOMG I'm so excited! I can't wait to start building off of it! 💃 🕺
|
||
// 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) |
There was a problem hiding this comment.
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! 😀
/lgtm |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED Approval requirements bypassed by manually added approval. This pull-request has been approved by: jberkhahn The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
Closes: #2111
@carolynvs this is an initial work to add command to svcat for creating user-defined classes. By now, it should handle only cluster-scoped classes. It is for sure missing some svcat command level tests and I am unsure what should be the return to the user in case of success: display created class name; display created class uuid; display all class info like 'get' command; some other thing. If you have sometime to review the current approach and suggest command output I can work on missing tests.