fix(helm): add log of failing test pod to 'helm test'

When 'helm test' fails, this will print the log of the failing test pods

Closes #1957
pull/3844/head
LiPing Gao 8 years ago
parent 55ec0709d5
commit 6a86e5249b

@ -47,12 +47,14 @@ import (
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
batchinternal "k8s.io/kubernetes/pkg/apis/batch" batchinternal "k8s.io/kubernetes/pkg/apis/batch"
"k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/core"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/conditions" "k8s.io/kubernetes/pkg/client/conditions"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/validation" "k8s.io/kubernetes/pkg/kubectl/validation"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
"os"
) )
const ( const (
@ -678,6 +680,38 @@ func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader,
return status, nil return status, nil
} }
//GetPodLogs print the log of a pod to standard output
func (c *Client) GetPodLogs(namespace string, reader io.Reader, timeout time.Duration) error {
infos, err := c.Build(namespace, reader)
if err != nil {
return err
}
info := infos[0]
kind := info.Mapping.GroupVersionKind.Kind
if kind != "Pod" {
return fmt.Errorf("%s is not a Pod", info.Name)
}
pod := info.Object.(*core.Pod)
cl, err := c.ClientSet()
if err != nil {
return err
}
log := cl.Core().Pods(namespace).GetLogs(pod.Name, &api.PodLogOptions{})
readCloser, err := log.Stream()
if err != nil {
return err
}
defer readCloser.Close()
_, err = io.Copy(os.Stdout, readCloser)
return err
}
func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Info) error { func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Info) error {
w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion) w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion)
if err != nil { if err != nil {

@ -62,6 +62,12 @@ func (env *Environment) getTestPodStatus(test *test) (core.PodPhase, error) {
return status, err return status, err
} }
func (env *Environment) getTestPodsLogs(test *test) error {
b := bytes.NewBufferString(test.manifest)
err := env.KubeClient.GetPodLogs(env.Namespace, b, time.Duration(env.Timeout)*time.Second)
return err
}
func (env *Environment) streamResult(r *release.TestRun) error { func (env *Environment) streamResult(r *release.TestRun) error {
switch r.Status { switch r.Status {
case release.TestRun_SUCCESS: case release.TestRun_SUCCESS:

@ -26,6 +26,7 @@ import (
"k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services" "k8s.io/helm/pkg/proto/hapi/services"
tillerEnv "k8s.io/helm/pkg/tiller/environment" tillerEnv "k8s.io/helm/pkg/tiller/environment"
"time"
) )
func TestCreateTestPodSuccess(t *testing.T) { func TestCreateTestPodSuccess(t *testing.T) {
@ -143,6 +144,10 @@ type getFailingKubeClient struct {
tillerEnv.PrintingKubeClient tillerEnv.PrintingKubeClient
} }
func (p *getFailingKubeClient) GetPodLogs(namespace string, reader io.Reader, timeout time.Duration) error {
return nil
}
func newGetFailingKubeClient() *getFailingKubeClient { func newGetFailingKubeClient() *getFailingKubeClient {
return &getFailingKubeClient{ return &getFailingKubeClient{
PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard}, PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard},
@ -157,6 +162,10 @@ type deleteFailingKubeClient struct {
tillerEnv.PrintingKubeClient tillerEnv.PrintingKubeClient
} }
func (p *deleteFailingKubeClient) GetPodLogs(namespace string, reader io.Reader, timeout time.Duration) error {
return nil
}
func newDeleteFailingKubeClient() *deleteFailingKubeClient { func newDeleteFailingKubeClient() *deleteFailingKubeClient {
return &deleteFailingKubeClient{ return &deleteFailingKubeClient{
PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard}, PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard},
@ -171,6 +180,10 @@ type createFailingKubeClient struct {
tillerEnv.PrintingKubeClient tillerEnv.PrintingKubeClient
} }
func (p *createFailingKubeClient) GetPodLogs(namespace string, reader io.Reader, timeout time.Duration) error {
return nil
}
func newCreateFailingKubeClient() *createFailingKubeClient { func newCreateFailingKubeClient() *createFailingKubeClient {
return &createFailingKubeClient{ return &createFailingKubeClient{
PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard}, PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard},

@ -95,7 +95,12 @@ func (ts *TestSuite) Run(env *Environment) error {
status, err = env.getTestPodStatus(test) status, err = env.getTestPodStatus(test)
if err != nil { if err != nil {
resourceCleanExit = false resourceCleanExit = false
if streamErr := env.streamError(test.result.Info); streamErr != nil { streamErr := env.streamError(test.result.Info)
logErr := env.getTestPodsLogs(test)
if logErr != nil {
fmt.Println("Fail to get log for the failing test")
}
if streamErr != nil {
return streamErr return streamErr
} }
} }

@ -318,6 +318,10 @@ type podSucceededKubeClient struct {
tillerEnv.PrintingKubeClient tillerEnv.PrintingKubeClient
} }
func (p *podSucceededKubeClient) GetPodLogs(namespace string, reader io.Reader, timeout time.Duration) error {
return nil
}
func newPodSucceededKubeClient() *podSucceededKubeClient { func newPodSucceededKubeClient() *podSucceededKubeClient {
return &podSucceededKubeClient{ return &podSucceededKubeClient{
PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard}, PrintingKubeClient: tillerEnv.PrintingKubeClient{Out: ioutil.Discard},
@ -338,6 +342,10 @@ func newPodFailedKubeClient() *podFailedKubeClient {
} }
} }
func (p *podFailedKubeClient) GetPodLogs(namespace string, reader io.Reader, timeout time.Duration) error {
return nil
}
func (p *podFailedKubeClient) WaitAndGetCompletedPodPhase(ns string, r io.Reader, timeout time.Duration) (core.PodPhase, error) { func (p *podFailedKubeClient) WaitAndGetCompletedPodPhase(ns string, r io.Reader, timeout time.Duration) (core.PodPhase, error) {
return core.PodFailed, nil return core.PodFailed, nil
} }

@ -143,6 +143,9 @@ type KubeClient interface {
// WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase // WaitAndGetCompletedPodPhase waits up to a timeout until a pod enters a completed phase
// and returns said phase (PodSucceeded or PodFailed qualify). // and returns said phase (PodSucceeded or PodFailed qualify).
WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (core.PodPhase, error)
// GetPodLogs gets the log of a pod and print it out to standard output
GetPodLogs(namespace string, reader io.Reader, timeout time.Duration) error
} }
// PrintingKubeClient implements KubeClient, but simply prints the reader to // PrintingKubeClient implements KubeClient, but simply prints the reader to
@ -151,6 +154,12 @@ type PrintingKubeClient struct {
Out io.Writer Out io.Writer
} }
//GetPodLogs prints the values of what would be created with a real KubeClient.
func (p *PrintingKubeClient) GetPodLogs(namespace string, r io.Reader, timeout time.Duration) error {
_, err := io.Copy(p.Out, r)
return err
}
// Create prints the values of what would be created with a real KubeClient. // Create prints the values of what would be created with a real KubeClient.
func (p *PrintingKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error { func (p *PrintingKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error {
_, err := io.Copy(p.Out, r) _, err := io.Copy(p.Out, r)

@ -40,6 +40,10 @@ func (e *mockEngine) Render(chrt *chart.Chart, v chartutil.Values) (map[string]s
type mockKubeClient struct{} type mockKubeClient struct{}
func (k *mockKubeClient) GetPodLogs(namespace string, r io.Reader, timeout time.Duration) error {
return nil
}
func (k *mockKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error { func (k *mockKubeClient) Create(ns string, r io.Reader, timeout int64, shouldWait bool) error {
return nil return nil
} }

@ -363,6 +363,10 @@ type mockHooksKubeClient struct {
Resources map[string]*mockHooksManifest Resources map[string]*mockHooksManifest
} }
func (kc *mockHooksKubeClient) GetPodLogs(namespace string, r io.Reader, timeout time.Duration) error {
return nil
}
var errResourceExists = errors.New("resource already exists") var errResourceExists = errors.New("resource already exists")
func (kc *mockHooksKubeClient) makeManifest(r io.Reader) (*mockHooksManifest, error) { func (kc *mockHooksKubeClient) makeManifest(r io.Reader) (*mockHooksManifest, error) {

Loading…
Cancel
Save