Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to disable terraform's output when running test #358

Closed
owulveryck opened this issue Aug 30, 2019 · 10 comments · Fixed by #1325
Closed

how to disable terraform's output when running test #358

owulveryck opened this issue Aug 30, 2019 · 10 comments · Fixed by #1325
Labels
enhancement New feature or request

Comments

@owulveryck
Copy link

owulveryck commented Aug 30, 2019

Hello and thank you for you tool.

I am using it to write some tests against my infrastructure.
I would like to inhibit the output of terraform while running go test and use the output of the functions to display what happened only if something fails:

output, err := terraform.InitAndPlanE(&testing.T{}, terraformOptions)
if err != nil {
    t.Log(output)
    t.Fail()
}

If the test succeeds, a go test will only return
PASS

Is there a way to easily achieve that?

@owulveryck
Copy link
Author

I managed to do it by tweaking the logger package.

The patch is:

--- a/modules/logger/logger.go
+++ b/modules/logger/logger.go
@@ -11,6 +11,13 @@ import (
        "time"
 )

+var output io.Writer = os.Stdout
+
+// Use a new writer to send the logs to
+func Use(w io.Writer) {
+       output = w
+}
+
 // Logf logs the given format and arguments, formatted using fmt.Sprintf, to stdout, along with a timestamp and information
 // about what test and file is doing the logging. This is an alternative to t.Logf that logs to stdout immediately,
 // rather than buffering all log output and only displaying it at the very end of the test. This is useful because:
@@ -29,14 +36,14 @@ import (
 // Note that there is a proposal to improve t.Logf (https://github.com/golang/go/issues/24929), but until that's
 // implemented, this method is our best bet.
 func Logf(t *testing.T, format string, args ...interface{}) {
-       DoLog(t, 2, os.Stdout, fmt.Sprintf(format, args...))
+       DoLog(t, 2, output, fmt.Sprintf(format, args...))
 }

 // Log logs the given arguments to stdout, along with a timestamp and information about what test and file is doing the
 // logging. This is an alternative to t.Logf that logs to stdout immediately, rather than buffering all log output and
 // only displaying it at the very end of the test. See the Logf method for more info.
 func Log(t *testing.T, args ...interface{}) {
-       DoLog(t, 2, os.Stdout, args...)
+       DoLog(t, 2, output, args...)
 }

 // DoLog logs the given arguments to the given writer, along with a timestamp and information about what test and file is

In my tests, I simply do a

logger.Use(ioutil.Discard)

if I do not want the logs.

WDYT? Do you want me to submit a PR?

@brikis98
Copy link
Member

Neat workaround, but I think that would suppress logging for everything in Terratest globally, which doesn't offer the granularity we need. Perhaps add some sort of logging level to terraform.Options, packer.Options, and other similar Options structs and pass it through?

@brikis98 brikis98 added enhancement New feature or request help wanted labels Aug 30, 2019
@owulveryck
Copy link
Author

What kind of granularity do you usually need?
Every test can use and set the Use function as needed.
On top of that, the API of the module/terraform is very flexible and catch the output anyway.
What do I miss? (I am new to terratest)

@brikis98
Copy link
Member

brikis98 commented Sep 2, 2019

Every test can use and set the Use function as needed.

Yes, but tests run in parallel, and this is a global setting, so tests will end up overwriting each other's settings.

@eschwartz
Copy link

Any update on this?

This is causing some pain in our automated test runs. We have tests that run OutputAll() against modules with a large number of outputs. When we run them in a CI system (Azure Devops), the CI tool truncates the test logs, and will not show us the actual test failure logs.

@tomaszsek
Copy link

Neat workaround, but I think that would suppress logging for everything in Terratest globally, which doesn't offer the granularity we need. Perhaps add some sort of logging level to terraform.Options, packer.Options, and other similar Options structs and pass it through?

That's is what I'm looking for. It's the best solution for me.

@mattolenik
Copy link

mattolenik commented Mar 7, 2020

I think the logger package should be utilizing Go's built in log package, and the various Options structs should allow the passing in of a custom Go logger. This would let you specify whatever io.Writer you want, be it stdout, stderr, or something else entirely, like GinkgoWriter when using the Ginkgo test framework. It would be granular down the specific packages you're using, each option struct can have the same or different logger instance.

I also think packages like http-helper also need some way to adjust logging output. I'm using HTTPDoWithRetryE to wait for a service to be ready, and when it does, http-helper dumps the entire HTML document to stdout, which is a bit absurd (edit: in my case I realized I could fix this by using the HEAD method, but even then there's quite a bit of log noise). For that package I think it would expose some kind of HTTPHelper struct which could be initialized with a custom logger (and any other values, like testing.T), and the existing functions would be attached to that struct as methods. Compatibility could be maintained by extracting the existing functions into private functions that require an instance of said HTTPHelper struct, then have the existing public functions call into those with a default instance.

@tommyknows
Copy link
Contributor

tommyknows commented Apr 15, 2020

Also, Go's Logf function now does streaming during the test:
https://go-review.googlesource.com/c/go/+/127120

That's landed in Go 1.14, can confirm that this works.

Would be great to support some kind of io.Writer and the Logf function at least.

I'd propose - and if people agree, would start to implement - it with the following interface:

type Logger interface {
        Logf(t testing.TestingT, format string, args ...interface{})
}

Complete, "minimal" usage example:

package logger

import (
        "fmt"
        "io"
        "os"
        "testing"
        "time"

        tftesting "github.com/gruntwork-io/terratest/modules/testing"
)

// bareLogger uses *testing.T to log, by doing a type assertion.
type bareLogger struct{}

func (b *bareLogger) Logf(t tftesting.TestingT, format string, args ...interface{}) {
        if tt, ok := t.(*testing.T); ok {
                tt.Logf(format, args...)
        }
}

func TestLogger(t *testing.T) {
        RegisterLogger(&bareLogger{})
        RegisterLogger(&customLogger{os.Stdout})

        DoLog(t, "%s world", "hello")
        time.Sleep(time.Second * 3)
        DoLog(t, "%s world", "goodbye")

}

type customLogger struct {
        io.Writer
}

func (c *customLogger) Logf(_ tftesting.TestingT, format string, args ...interface{}) {
        _, _ = c.Write([]byte(fmt.Sprintf("CustomLogger: "+format+"\n", args...)))
}

type Logger interface {
        Logf(t tftesting.TestingT, format string, args ...interface{})
}

func DoLog(t tftesting.TestingT, format string, args ...interface{}) {
        for _, l := range loggers {
                l.Logf(t, format, args...)
        }
}

var loggers []Logger

func RegisterLogger(l Logger) {
        loggers = append(loggers, l)
}

I'd add a field to terraform.Options (Loggers []logger.Logger) which then get used to print the command output.

@tommyknows tommyknows mentioned this issue Apr 19, 2020
@blame19
Copy link

blame19 commented Mar 23, 2021

It would be good to have the same behavior for the k8s module.

retpolanne added a commit to retpolanne/terratest that referenced this issue Jul 24, 2023
This commit adds the custom logging facility to kubectl options and to
the Kubectl Run with output that can be used to suppress stdout from
Kubectl run if needed.

The logging facility was introduced on gruntwork-io#510 but it missed kubectl
options.

Fixes gruntwork-io#358 since @blame19 recalled that the behavior was missing on the
k8s module.

Signed-off-by: Anne Macedo <retpolanne@posteo.net>
@retpolanne
Copy link
Contributor

@blame19 sent a patch adding this behavior to k8s and kubectl.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants