opentofu/internal/genconfig/generate_config_write.go

85 lines
2.9 KiB
Go
Raw Normal View History

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package genconfig
import (
"fmt"
"io"
"os"
"github.com/opentofu/opentofu/internal/tfdiags"
)
func ShouldWriteConfig(out string) bool {
2023-05-30 20:00:41 -05:00
// No specified out file, so don't write anything.
return len(out) != 0
}
func ValidateTargetFile(out string) (diags tfdiags.Diagnostics) {
if _, err := os.Stat(out); !os.IsNotExist(err) {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Target generated file already exists",
"OpenTofu can only write generated config into a new file. Either choose a different target location or move all existing configuration out of the target file, delete it and try again."))
}
return diags
}
type Change struct {
Addr string
ImportID string
GeneratedConfig string
}
func (c *Change) MaybeWriteConfig(writer io.Writer, out string) (io.Writer, bool, tfdiags.Diagnostics) {
var wroteConfig bool
var diags tfdiags.Diagnostics
if len(c.GeneratedConfig) > 0 {
if writer == nil {
// Lazily create the generated file, in case we have no
// generated config to create.
if w, err := os.Create(out); err != nil {
if os.IsPermission(err) {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to create target generated file",
fmt.Sprintf("OpenTofu did not have permission to create the generated file (%s) in the target directory. Please modify permissions over the target directory, and try again.", out)))
return nil, false, diags
}
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to create target generated file",
2023-09-21 10:15:32 -05:00
fmt.Sprintf("OpenTofu could not create the generated file (%s) in the target directory: %v. Depending on the error message, this may be a bug in OpenTofu itself. If so, please report it!", out, err)))
return nil, false, diags
} else {
writer = w
}
header := "# __generated__ by OpenTofu\n# Please review these resources and move them into your main configuration files.\n"
// Missing the header from the file, isn't the end of the world
// so if this did return an error, then we will just ignore it.
_, _ = writer.Write([]byte(header))
}
header := "\n# __generated__ by OpenTofu"
if len(c.ImportID) > 0 {
header += fmt.Sprintf(" from %q", c.ImportID)
}
header += "\n"
if _, err := writer.Write([]byte(fmt.Sprintf("%s%s\n", header, c.GeneratedConfig))); err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Warning,
"Failed to save generated config",
2023-09-21 10:15:32 -05:00
fmt.Sprintf("OpenTofu encountered an error while writing generated config: %v. The config for %s must be created manually before applying. Depending on the error message, this may be a bug in OpenTofu itself. If so, please report it!", err, c.Addr)))
}
wroteConfig = true
plannable import: safer config generation and schema filters (#33232) * genconfig: fix nil nested block panic * genconfig: null NestingSingle blocks should be absent A NestingSingle block that is null in state should be completely absent from config. * configschema: make FilterOr variadic * configschema: apply filters to nested types * configschema: filter helper/schema id attribute The legacy SDK adds an Optional+Computed "id" attribute to the resource schema even if not defined in provider code. During validation, however, the presence of an extraneous "id" attribute in config will cause an error. Remove this attribute so we do not generate an "id" attribute where there is a risk that it is not in the real resource schema. * configschema: filter test * terraform: do not pre-validate generated config Config generated from a resource's import state may fail validation in the case of schema behaviours such as ExactlyOneOf and ConflictsWith. We don't want to fail the plan now, because that would give the user no way to proceed and fix the config to make it valid. We allow the plan to complete and output the generated config. * generate config alongside import process Rather than waiting until we call `plan()`, generate the configuration at the point of the import call, so we have the necessary data to return in case planning fails later. The `plan` and `state` predeclared variables in the plan() method were obfuscating the actual return of nil throughout, so those identifiers were removed for clarity. * move generateHCLStringAttributes closer to caller * store generated config in plan on error * test for config gen with error * add simple warning when generating config --------- Co-authored-by: James Bardin <j.bardin@gmail.com>
2023-05-24 05:16:05 -05:00
}
return writer, wroteConfig, diags
}