hashicorp / terraform

Terraform enables you to safely and predictably create, change, and improve infrastructure. It is a source-available tool that codifies APIs into declarative configuration files that can be shared amongst team members, treated as code, edited, reviewed, and versioned.
https://www.terraform.io/
Other
41.66k stars 9.41k forks source link

Print retryLogHook messages to stderr #35159

Open ben-z opened 1 month ago

ben-z commented 1 month ago

Terraform Version

Terraform v1.8.2
on linux_amd64

Terraform Configuration Files

terraform {
  cloud {
    organization = "..."

    workspaces {
      name = "..."
    }
  }
}

// ...

Debug Output

Not relevant

Expected Behavior

terraform -output json should output only JSON.

Actual Behavior

The output is not pure json. It contains some error messages from intermittent (retriable) errors.

> terraform -output json
There was an error connecting to HCP Terraform. Please do not exit Terraform to prevent data loss!
Trying to restore the connection...

Still trying to restore the connection... (3s elapsed)
{}

These are the culprit messages: https://github.com/hashicorp/terraform/blob/dc3a040a4164ffba863cef7bd216c4f49e30e40b/internal/command/views/cloud.go#L51-L56

This makes it nontrivial for downstream applications to parse the output.

Steps to Reproduce

  1. terraform -output json
  2. When the connection to the backend is suboptimal, additional messages show up.

Additional Context

No response

References

No response

ben-z commented 1 month ago

I'm currently using a quick and dirty workaround: next(extract_json_objects(stdout))

def extract_json_objects(text, decoder=JSONDecoder()):
    """
    Find JSON objects in text, and yield the decoded JSON data

    Does not attempt to look for JSON arrays, text, or other JSON types outside
    of a parent JSON object.

    Derived from https://stackoverflow.com/a/54235803

    This is useful for handling JSON objects embedded in a string, such as
    when this bug pollutes the output:
    https://github.com/hashicorp/terraform/issues/35159
    """
    pos = 0
    while True:
        match = text.find("{", pos)
        if match == -1:
            break
        try:
            result, index = decoder.raw_decode(text[match:])
            yield result
            pos = match + index
        except ValueError:
            pos = match + 1