refactor(service): Removed io.writer from of KnClient.WaitForService() (#248)

This puts all the console output to the command, where it belongs too.
One could add a ProgressHandler for more fine granular feedback (like suggested in #234) but for now this is not needed.
This commit is contained in:
Roland Huß 2019-07-08 20:57:34 +02:00 committed by Knative Prow Robot
parent 98184eafbc
commit 2ae037f3be
6 changed files with 35 additions and 52 deletions

View File

@ -6,8 +6,9 @@ Knative client
Manage your Knative building blocks: Manage your Knative building blocks:
* [Serving](https://github.com/knative/serving/tree/master): Manage your services and release new software to them. Serving: Manage your services and release new software to them.
* [Eventing](https://github.com/knative/eventing/tree/master): Manage event subscriptions and channels. Connect event sources. Build: Create builds and keep track of their results.
Eventing: Manage event subscriptions and channels. Connect up event sources.
### Options ### Options

View File

@ -104,10 +104,16 @@ func NewServiceCreateCommand(p *commands.KnParams) *cobra.Command {
} }
if !waitFlags.Async { if !waitFlags.Async {
err := client.WaitForService(name, time.Duration(waitFlags.TimeoutInSeconds)*time.Second, cmd.OutOrStdout()) out := cmd.OutOrStdout()
fmt.Fprintf(out, "Waiting for service '%s' to become ready ... ", name)
flush(out)
err := client.WaitForService(name, time.Duration(waitFlags.TimeoutInSeconds)*time.Second)
if err != nil { if err != nil {
fmt.Fprintln(out)
return err return err
} }
fmt.Fprintln(out, "OK")
return showUrl(client, name, namespace, cmd.OutOrStdout()) return showUrl(client, name, namespace, cmd.OutOrStdout())
} }
@ -120,6 +126,17 @@ func NewServiceCreateCommand(p *commands.KnParams) *cobra.Command {
return serviceCreateCommand return serviceCreateCommand
} }
// Duck type for writers having a flush
type flusher interface {
Flush() error
}
func flush(out io.Writer) {
if flusher, ok := out.(flusher); ok {
flusher.Flush()
}
}
func createService(client v1alpha1.KnClient, service *serving_v1alpha1_api.Service, namespace string, out io.Writer) error { func createService(client v1alpha1.KnClient, service *serving_v1alpha1_api.Service, namespace string, out io.Writer) error {
err := client.CreateService(service) err := client.CreateService(service)
if err != nil { if err != nil {

View File

@ -16,7 +16,6 @@ package v1alpha1
import ( import (
"fmt" "fmt"
"io"
"time" "time"
"github.com/knative/pkg/apis" "github.com/knative/pkg/apis"
@ -53,7 +52,7 @@ type KnClient interface {
DeleteService(name string) error DeleteService(name string) error
// Wait for a service to become ready, but not longer than provided timeout // Wait for a service to become ready, but not longer than provided timeout
WaitForService(name string, timeout time.Duration, out io.Writer) error WaitForService(name string, timeout time.Duration) error
// Get a revision by name // Get a revision by name
GetRevision(name string) (*v1alpha1.Revision, error) GetRevision(name string) (*v1alpha1.Revision, error)
@ -188,9 +187,9 @@ func (cl *knClient) DeleteService(serviceName string) error {
} }
// Wait for a service to become ready, but not longer than provided timeout // Wait for a service to become ready, but not longer than provided timeout
func (cl *knClient) WaitForService(name string, timeout time.Duration, out io.Writer) error { func (cl *knClient) WaitForService(name string, timeout time.Duration) error {
waitForReady := newServiceWaitForReady(cl.client.Services(cl.namespace).Watch) waitForReady := newServiceWaitForReady(cl.client.Services(cl.namespace).Watch)
return waitForReady.Wait(name, timeout, out) return waitForReady.Wait(name, timeout)
} }
// Get a revision by name // Get a revision by name

View File

@ -15,7 +15,6 @@
package v1alpha1 package v1alpha1
import ( import (
"bytes"
"fmt" "fmt"
"testing" "testing"
"time" "time"
@ -357,8 +356,7 @@ func TestWaitForService(t *testing.T) {
}) })
t.Run("wait on a service to become ready with success", func(t *testing.T) { t.Run("wait on a service to become ready with success", func(t *testing.T) {
buf := new(bytes.Buffer) err := client.WaitForService(serviceName, 60*time.Second)
err := client.WaitForService(serviceName, 60*time.Second, buf)
assert.NilError(t, err) assert.NilError(t, err)
}) })
} }

View File

@ -16,7 +16,6 @@ package wait
import ( import (
"fmt" "fmt"
"io"
"time" "time"
"github.com/knative/pkg/apis" "github.com/knative/pkg/apis"
@ -40,7 +39,7 @@ type WaitForReady interface {
// Wait on resource the resource with this name until a given timeout // Wait on resource the resource with this name until a given timeout
// and write status out on writer // and write status out on writer
Wait(name string, timeout time.Duration, out io.Writer) error Wait(name string, timeout time.Duration) error
} }
// Create watch which is used when waiting for Ready condition // Create watch which is used when waiting for Ready condition
@ -62,21 +61,17 @@ func NewWaitForReady(kind string, watchFunc WatchFunc, extractor ConditionsExtra
// `watchFunc` creates the actual watch, `kind` is the type what your are watching for // `watchFunc` creates the actual watch, `kind` is the type what your are watching for
// (e.g. "service"), `timeout` is a timeout after which the watch should be cancelled if no // (e.g. "service"), `timeout` is a timeout after which the watch should be cancelled if no
// target state has been entered yet and `out` is used for printing out status messages // target state has been entered yet and `out` is used for printing out status messages
func (w *waitForReadyConfig) Wait(name string, timeout time.Duration, out io.Writer) error { func (w *waitForReadyConfig) Wait(name string, timeout time.Duration) error {
opts := v1.ListOptions{ opts := v1.ListOptions{
FieldSelector: fields.OneTermEqualSelector("metadata.name", name).String(), FieldSelector: fields.OneTermEqualSelector("metadata.name", name).String(),
} }
addWatchTimeout(&opts, timeout) addWatchTimeout(&opts, timeout)
fmt.Fprintf(out, "Waiting for %s '%s' to become ready ... ", w.kind, name)
flush(out)
floatingTimeout := timeout floatingTimeout := timeout
for { for {
start := time.Now() start := time.Now()
retry, timeoutReached, err := w.waitForReadyCondition(opts, name, floatingTimeout) retry, timeoutReached, err := w.waitForReadyCondition(opts, name, floatingTimeout)
if err != nil { if err != nil {
fmt.Fprintln(out)
return err return err
} }
floatingTimeout = floatingTimeout - time.Since(start) floatingTimeout = floatingTimeout - time.Since(start)
@ -88,8 +83,6 @@ func (w *waitForReadyConfig) Wait(name string, timeout time.Duration, out io.Wri
// restart loop // restart loop
continue continue
} }
fmt.Fprintln(out, "OK")
return nil return nil
} }
} }
@ -105,17 +98,6 @@ func addWatchTimeout(opts *v1.ListOptions, timeout time.Duration) {
opts.TimeoutSeconds = &timeOutWatchSeconds opts.TimeoutSeconds = &timeOutWatchSeconds
} }
// Duck type for writers having a flush
type flusher interface {
Flush() error
}
func flush(out io.Writer) {
if flusher, ok := out.(flusher); ok {
flusher.Flush()
}
}
func (w *waitForReadyConfig) waitForReadyCondition(opts v1.ListOptions, name string, timeout time.Duration) (bool, bool, error) { func (w *waitForReadyConfig) waitForReadyCondition(opts v1.ListOptions, name string, timeout time.Duration) (bool, bool, error) {
watcher, err := w.watchFunc(opts) watcher, err := w.watchFunc(opts)

View File

@ -15,8 +15,6 @@
package wait package wait
import ( import (
"bytes"
"strings"
"testing" "testing"
"time" "time"
@ -32,14 +30,12 @@ type waitForReadyTestCase struct {
events []watch.Event events []watch.Event
timeout time.Duration timeout time.Duration
errorExpected bool errorExpected bool
messageContent []string
} }
func TestAddWaitForReady(t *testing.T) { func TestAddWaitForReady(t *testing.T) {
for i, tc := range prepareTestCases("test-service") { for i, tc := range prepareTestCases("test-service") {
fakeWatchApi := NewFakeWatch(tc.events) fakeWatchApi := NewFakeWatch(tc.events)
outBuffer := new(bytes.Buffer)
waitForReady := NewWaitForReady( waitForReady := NewWaitForReady(
"blub", "blub",
@ -50,7 +46,7 @@ func TestAddWaitForReady(t *testing.T) {
return apis.Conditions(obj.(*v1alpha1.Service).Status.Conditions), nil return apis.Conditions(obj.(*v1alpha1.Service).Status.Conditions), nil
}) })
fakeWatchApi.Start() fakeWatchApi.Start()
err := waitForReady.Wait("foobar", tc.timeout, outBuffer) err := waitForReady.Wait("foobar", tc.timeout)
close(fakeWatchApi.eventChan) close(fakeWatchApi.eventChan)
if !tc.errorExpected && err != nil { if !tc.errorExpected && err != nil {
@ -60,16 +56,6 @@ func TestAddWaitForReady(t *testing.T) {
if tc.errorExpected && err == nil { if tc.errorExpected && err == nil {
t.Errorf("%d: No error but expected one", i) t.Errorf("%d: No error but expected one", i)
} }
txtToCheck := outBuffer.String()
if err != nil {
txtToCheck = err.Error()
}
for _, msg := range tc.messageContent {
if !strings.Contains(txtToCheck, msg) {
t.Errorf("%d: '%s' does not contain expected part %s", i, txtToCheck, msg)
}
}
if fakeWatchApi.StopCalled != 1 { if fakeWatchApi.StopCalled != 1 {
t.Errorf("%d: Exactly one 'stop' should be called, but got %d", i, fakeWatchApi.StopCalled) t.Errorf("%d: Exactly one 'stop' should be called, but got %d", i, fakeWatchApi.StopCalled)
@ -81,10 +67,10 @@ func TestAddWaitForReady(t *testing.T) {
// Test cases which consists of a series of events to send and the expected behaviour. // Test cases which consists of a series of events to send and the expected behaviour.
func prepareTestCases(name string) []waitForReadyTestCase { func prepareTestCases(name string) []waitForReadyTestCase {
return []waitForReadyTestCase{ return []waitForReadyTestCase{
{peNormal(name), time.Second, false, []string{"OK", "foobar", "blub"}}, {peNormal(name), time.Second, false},
{peError(name), time.Second, true, []string{"FakeError"}}, {peError(name), time.Second, true},
{peTimeout(name), time.Second, true, []string{"timeout"}}, {peTimeout(name), time.Second, true},
{peWrongGeneration(name), time.Second, true, []string{"timeout"}}, {peWrongGeneration(name), time.Second, true},
} }
} }