feat(wait): add sync revision delete operation (#688)

This commit is contained in:
David Simansky 2020-02-29 15:09:51 +01:00 committed by GitHub
parent 034a9b387e
commit b14dc2dc7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 66 additions and 9 deletions

View File

@ -21,8 +21,11 @@ kn revision delete NAME [flags]
### Options
```
--async DEPRECATED: please use --no-wait instead. Delete revision and don't wait for it to be deleted.
-h, --help help for delete
-n, --namespace string Specify the namespace to operate in.
--no-wait Delete revision and don't wait for it to be deleted.
--wait-timeout int Seconds to wait before giving up on waiting for revision to be deleted. (default 600)
```
### Options inherited from parent commands

View File

@ -17,6 +17,7 @@ package revision
import (
"errors"
"fmt"
"time"
"github.com/spf13/cobra"
@ -25,6 +26,8 @@ import (
// NewRevisionDeleteCommand represent 'revision delete' command
func NewRevisionDeleteCommand(p *commands.KnParams) *cobra.Command {
var waitFlags commands.WaitFlags
RevisionDeleteCommand := &cobra.Command{
Use: "delete NAME",
Short: "Delete a revision.",
@ -45,7 +48,11 @@ func NewRevisionDeleteCommand(p *commands.KnParams) *cobra.Command {
}
for _, name := range args {
err = client.DeleteRevision(name)
timeout := time.Duration(0)
if !waitFlags.NoWait {
timeout = time.Duration(waitFlags.TimeoutInSeconds) * time.Second
}
err = client.DeleteRevision(name, timeout)
if err != nil {
fmt.Fprintf(cmd.OutOrStdout(), "%s.\n", err)
} else {
@ -56,5 +63,6 @@ func NewRevisionDeleteCommand(p *commands.KnParams) *cobra.Command {
},
}
commands.AddNamespaceFlags(RevisionDeleteCommand.Flags(), false)
waitFlags.AddConditionWaitFlags(RevisionDeleteCommand, commands.WaitDefaultTimeout, "Delete", "revision", "deleted")
return RevisionDeleteCommand
}

View File

@ -15,14 +15,19 @@
package revision
import (
"errors"
"testing"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/watch"
clienttesting "k8s.io/client-go/testing"
"knative.dev/client/pkg/kn/commands"
"knative.dev/client/pkg/util"
"knative.dev/client/pkg/wait"
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
)
func fakeRevisionDelete(args []string) (action clienttesting.Action, name string, output string, err error) {
@ -35,6 +40,17 @@ func fakeRevisionDelete(args []string) (action clienttesting.Action, name string
name = deleteAction.GetName()
return true, nil, nil
})
fakeServing.AddWatchReactor("revisions",
func(a clienttesting.Action) (bool, watch.Interface, error) {
watchAction := a.(clienttesting.WatchAction)
_, found := watchAction.GetWatchRestrictions().Fields.RequiresExactMatch("metadata.name")
if !found {
return true, nil, errors.New("no field selector on metadata.name found")
}
w := wait.NewFakeWatch(getRevisionDeleteEvents("test-revision"))
w.Start()
return true, w, nil
})
cmd.SetArgs(args)
err = cmd.Execute()
if err != nil {
@ -79,3 +95,10 @@ func TestMultipleRevisionDelete(t *testing.T) {
assert.Check(t, util.ContainsAll(output, "Revision", revName2, "deleted", "namespace", commands.FakeNamespace))
assert.Check(t, util.ContainsAll(output, "Revision", revName3, "deleted", "namespace", commands.FakeNamespace))
}
func getRevisionDeleteEvents(name string) []watch.Event {
return []watch.Event{
{watch.Added, &servingv1.Revision{ObjectMeta: metav1.ObjectMeta{Name: name}}},
{watch.Deleted, &servingv1.Revision{ObjectMeta: metav1.ObjectMeta{Name: name}}},
}
}

View File

@ -89,7 +89,7 @@ type KnServingClient interface {
ListRevisions(opts ...ListConfig) (*servingv1.RevisionList, error)
// Delete a revision
DeleteRevision(name string) error
DeleteRevision(name string, timeout time.Duration) error
// Get a route by its unique name
GetRoute(name string) (*servingv1.Route, error)
@ -177,6 +177,11 @@ func (cl *knServingClient) WatchService(name string, timeout time.Duration) (wat
cl.client.RESTClient(), cl.namespace, "services", name, timeout)
}
func (cl *knServingClient) WatchRevision(name string, timeout time.Duration) (watch.Interface, error) {
return wait.NewWatcher(cl.client.Revisions(cl.namespace).Watch,
cl.client.RESTClient(), cl.namespace, "revision", name, timeout)
}
// List services
func (cl *knServingClient) ListServices(config ...ListConfig) (*servingv1.ServiceList, error) {
serviceList, err := cl.client.Services(cl.namespace).List(ListConfigs(config).toListOptions())
@ -370,7 +375,25 @@ func getBaseRevision(cl KnServingClient, service *servingv1.Service) (*servingv1
}
// Delete a revision by name
func (cl *knServingClient) DeleteRevision(name string) error {
func (cl *knServingClient) DeleteRevision(name string, timeout time.Duration) error {
if timeout == 0 {
return cl.deleteRevision(name)
}
waitC := make(chan error)
go func() {
waitForEvent := wait.NewWaitForEvent("revision", cl.WatchRevision, func(evt *watch.Event) bool { return evt.Type == watch.Deleted })
err, _ := waitForEvent.Wait(name, timeout, wait.NoopMessageCallback())
waitC <- err
}()
err := cl.deleteRevision(name)
if err != nil {
return clienterrors.GetError(err)
}
return <-waitC
}
func (cl *knServingClient) deleteRevision(name string) error {
err := cl.client.Revisions(cl.namespace).Delete(name, &v1.DeleteOptions{})
if err != nil {
return clienterrors.GetError(err)

View File

@ -146,12 +146,12 @@ func (c *MockKnServingClient) ListRevisions(opts ...ListConfig) (*servingv1.Revi
}
// Delete a revision
func (sr *ServingRecorder) DeleteRevision(name interface{}, err error) {
sr.r.Add("DeleteRevision", []interface{}{name}, []interface{}{err})
func (sr *ServingRecorder) DeleteRevision(name, timeout interface{}, err error) {
sr.r.Add("DeleteRevision", []interface{}{name, timeout}, []interface{}{err})
}
func (c *MockKnServingClient) DeleteRevision(name string) error {
call := c.recorder.r.VerifyCall("DeleteRevision", name)
func (c *MockKnServingClient) DeleteRevision(name string, timeout time.Duration) error {
call := c.recorder.r.VerifyCall("DeleteRevision", name, timeout)
return mock.ErrorOrNil(call.Result[0])
}

View File

@ -40,7 +40,7 @@ func TestMockKnClient(t *testing.T) {
recorder.WaitForService("hello", time.Duration(10)*time.Second, wait.NoopMessageCallback(), nil, 10*time.Second)
recorder.GetRevision("hello", nil, nil)
recorder.ListRevisions(mock.Any(), nil, nil)
recorder.DeleteRevision("hello", nil)
recorder.DeleteRevision("hello", time.Duration(10)*time.Second, nil)
recorder.GetRoute("hello", nil, nil)
recorder.ListRoutes(mock.Any(), nil, nil)
recorder.GetConfiguration("hello", nil, nil)
@ -54,7 +54,7 @@ func TestMockKnClient(t *testing.T) {
client.WaitForService("hello", time.Duration(10)*time.Second, wait.NoopMessageCallback())
client.GetRevision("hello")
client.ListRevisions(WithName("blub"))
client.DeleteRevision("hello")
client.DeleteRevision("hello", time.Duration(10)*time.Second)
client.GetRoute("hello")
client.ListRoutes(WithName("blub"))
client.GetConfiguration("hello")