From b94608388e2fb958588078ab2c391bea9afc1505 Mon Sep 17 00:00:00 2001 From: Jeff Knurek Date: Mon, 5 Aug 2019 17:01:14 +0200 Subject: [PATCH] TEST: add option to dump logs during helm test Signed-off-by: Jeff Knurek --- _proto/hapi/services/tiller.proto | 2 ++ cmd/helm/release_testing.go | 3 +++ pkg/helm/option.go | 7 +++++++ pkg/kube/client.go | 18 ++++++++++++++++++ pkg/releasetesting/environment.go | 20 +++++++++++++++++++- pkg/tiller/environment/environment.go | 7 +++++++ pkg/tiller/release_testing.go | 4 ++++ 7 files changed, 60 insertions(+), 1 deletion(-) diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index d58c323d6..7ea3b5c9f 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -352,6 +352,8 @@ message TestReleaseRequest { bool parallel = 4; // maximum number of test pods to run in parallel uint32 max_parallel = 5; + // logs specifies whether or not to dump the logs from the test pods + bool logs = 6; } // TestReleaseResponse represents a message from executing a test diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index a0bf0b87c..17084d0a4 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -41,6 +41,7 @@ type releaseTestCmd struct { cleanup bool parallel bool maxParallel uint32 + logs bool } func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command { @@ -71,6 +72,7 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&rlsTest.cleanup, "cleanup", false, "Delete test pods upon completion") f.BoolVar(&rlsTest.parallel, "parallel", false, "Run test pods in parallel") f.Uint32Var(&rlsTest.maxParallel, "max", 20, "Maximum number of test pods to run in parallel") + f.BoolVar(&rlsTest.logs, "logs", false, "Dump the logs from test pods (this runs after all tests are complete, but before any cleanup") // set defaults from environment settings.InitTLS(f) @@ -85,6 +87,7 @@ func (t *releaseTestCmd) run() (err error) { helm.ReleaseTestCleanup(t.cleanup), helm.ReleaseTestParallel(t.parallel), helm.ReleaseTestMaxParallel(t.maxParallel), + helm.ReleaseTestLogs(t.logs), ) testErr := &testErr{} diff --git a/pkg/helm/option.go b/pkg/helm/option.go index 1374f53a7..1eb0f133c 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -241,6 +241,13 @@ func ReleaseTestMaxParallel(max uint32) ReleaseTestOption { } } +// ReleaseTestLogs is a boolean value representing whether to dump the logs from test pods +func ReleaseTestLogs(logs bool) ReleaseTestOption { + return func(opts *options) { + opts.testReq.Logs = logs + } +} + // RollbackTimeout specifies the number of seconds before kubernetes calls timeout func RollbackTimeout(timeout int64) RollbackOption { return func(opts *options) { diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 4d181d1ed..13be438e1 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -947,6 +947,24 @@ func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Inf return err } +// GetPodLogs takes pod name and namespace and returns the current logs (streaming is NOT enabled). +func (c *Client) GetPodLogs(name, ns string) (string, error) { + client, _ := c.KubernetesClientSet() + req := client.CoreV1().Pods(ns).GetLogs(name, &v1.PodLogOptions{}) + podLogs, err := req.Stream() + if err != nil { + return "", fmt.Errorf("error in opening log stream, got: %s", err) + } + defer podLogs.Close() + + buf := new(bytes.Buffer) + _, err = io.Copy(buf, podLogs) + if err != nil { + return "", fmt.Errorf("error in copy information from log stream to buf, got: %s", err) + } + return buf.String(), nil +} + func isPodComplete(event watch.Event) (bool, error) { o, ok := event.Object.(*v1.Pod) if !ok { diff --git a/pkg/releasetesting/environment.go b/pkg/releasetesting/environment.go index e4184b5f4..98a5267c1 100644 --- a/pkg/releasetesting/environment.go +++ b/pkg/releasetesting/environment.go @@ -96,7 +96,7 @@ func (env *Environment) streamError(info string) error { } func (env *Environment) streamFailed(name string) error { - msg := fmt.Sprintf("FAILED: %s, run `kubectl logs %s --namespace %s` for more info", name, name, env.Namespace) + msg := "FAILED: " + name return env.streamMessage(msg, release.TestRun_FAILURE) } @@ -126,3 +126,21 @@ func (env *Environment) DeleteTestPods(testManifests []string) { } } } + +func (env *Environment) GetLogs(testManifests []string) { + for _, testManifest := range testManifests { + infos, err := env.KubeClient.Build(env.Namespace, bytes.NewBufferString(testManifest)) + if err != nil { + env.streamError(err.Error()) + continue + } + podName := infos[0].Object.(*v1.Pod).Name + logs, err := env.KubeClient.GetPodLogs(podName, env.Namespace) + if err != nil { + env.streamError(err.Error()) + continue + } + msg := fmt.Sprintf("\nPOD LOGS: %s\n%s", podName, logs) + env.streamMessage(msg, release.TestRun_UNKNOWN) + } +} diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index d3a478ea0..8ce2119c8 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -175,6 +175,8 @@ type KubeClient interface { // and returns said phase (PodSucceeded or PodFailed qualify). WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, timeout time.Duration) (v1.PodPhase, error) + GetPodLogs(name, namespace string) (string, error) + WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error } @@ -255,6 +257,11 @@ func (p *PrintingKubeClient) WaitAndGetCompletedPodPhase(namespace string, reade return v1.PodUnknown, err } +// GetPodLogs implements KubeClient GetPodLogs. +func (p *PrintingKubeClient) GetPodLogs(name, ns string) (string, error) { + return "", nil +} + // WaitUntilCRDEstablished implements KubeClient WaitUntilCRDEstablished. func (p *PrintingKubeClient) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error { _, err := io.Copy(p.Out, reader) diff --git a/pkg/tiller/release_testing.go b/pkg/tiller/release_testing.go index 2e6728e5e..e546dfd63 100644 --- a/pkg/tiller/release_testing.go +++ b/pkg/tiller/release_testing.go @@ -69,6 +69,10 @@ func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream Results: tSuite.Results, } + if req.Logs { + testEnv.GetLogs(tSuite.TestManifests) + } + if req.Cleanup { testEnv.DeleteTestPods(tSuite.TestManifests) }