Integrate Encryption into State Backends (#1288)

Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
Christian Mesh 2024-03-04 09:25:14 -05:00 committed by GitHub
parent ac3ed86617
commit 2f5dcd5c0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
95 changed files with 530 additions and 362 deletions

View File

@ -24,6 +24,7 @@ import (
"github.com/opentofu/opentofu/internal/configs/configload"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/depsfile"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/plans/planfile"
"github.com/opentofu/opentofu/internal/states"
@ -54,7 +55,7 @@ var (
)
// InitFn is used to initialize a new backend.
type InitFn func() Backend
type InitFn func(encryption.StateEncryption) Backend
// Backend is the minimal interface that must be implemented to enable OpenTofu.
type Backend interface {

View File

@ -9,13 +9,14 @@ import (
"testing"
"github.com/opentofu/opentofu/internal/backend/remote-state/inmem"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/zclconf/go-cty/cty"
)
func TestDeprecateBackend(t *testing.T) {
deprecateMessage := "deprecated backend"
deprecatedBackend := deprecateBackend(
inmem.New(),
inmem.New(encryption.StateEncryptionDisabled()),
deprecateMessage,
)

View File

@ -12,6 +12,7 @@ import (
"github.com/hashicorp/terraform-svchost/disco"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
@ -54,24 +55,24 @@ func Init(services *disco.Disco) {
defer backendsLock.Unlock()
backends = map[string]backend.InitFn{
"local": func() backend.Backend { return backendLocal.New() },
"remote": func() backend.Backend { return backendRemote.New(services) },
"local": func(enc encryption.StateEncryption) backend.Backend { return backendLocal.New(enc) },
"remote": func(enc encryption.StateEncryption) backend.Backend { return backendRemote.New(services, enc) },
// Remote State backends.
"azurerm": func() backend.Backend { return backendAzure.New() },
"consul": func() backend.Backend { return backendConsul.New() },
"cos": func() backend.Backend { return backendCos.New() },
"gcs": func() backend.Backend { return backendGCS.New() },
"http": func() backend.Backend { return backendHTTP.New() },
"inmem": func() backend.Backend { return backendInmem.New() },
"kubernetes": func() backend.Backend { return backendKubernetes.New() },
"oss": func() backend.Backend { return backendOSS.New() },
"pg": func() backend.Backend { return backendPg.New() },
"s3": func() backend.Backend { return backendS3.New() },
"azurerm": func(enc encryption.StateEncryption) backend.Backend { return backendAzure.New(enc) },
"consul": func(enc encryption.StateEncryption) backend.Backend { return backendConsul.New(enc) },
"cos": func(enc encryption.StateEncryption) backend.Backend { return backendCos.New(enc) },
"gcs": func(enc encryption.StateEncryption) backend.Backend { return backendGCS.New(enc) },
"http": func(enc encryption.StateEncryption) backend.Backend { return backendHTTP.New(enc) },
"inmem": func(enc encryption.StateEncryption) backend.Backend { return backendInmem.New(enc) },
"kubernetes": func(enc encryption.StateEncryption) backend.Backend { return backendKubernetes.New(enc) },
"oss": func(enc encryption.StateEncryption) backend.Backend { return backendOSS.New(enc) },
"pg": func(enc encryption.StateEncryption) backend.Backend { return backendPg.New(enc) },
"s3": func(enc encryption.StateEncryption) backend.Backend { return backendS3.New(enc) },
// Terraform Cloud 'backend'
// This is an implementation detail only, used for the cloud package
"cloud": func() backend.Backend { return backendCloud.New(services) },
"cloud": func(enc encryption.StateEncryption) backend.Backend { return backendCloud.New(services, enc) },
}
RemovedBackends = map[string]string{

View File

@ -8,6 +8,8 @@ package init
import (
"reflect"
"testing"
"github.com/opentofu/opentofu/internal/encryption"
)
func TestInit_backend(t *testing.T) {
@ -36,7 +38,7 @@ func TestInit_backend(t *testing.T) {
if f == nil {
t.Fatalf("backend %q is not present; should be", b.Name)
}
bType := reflect.TypeOf(f()).String()
bType := reflect.TypeOf(f(encryption.StateEncryptionDisabled())).String()
if bType != b.Type {
t.Fatalf("expected backend %q to be %q, got: %q", b.Name, b.Type, bType)
}

View File

@ -18,6 +18,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/command/views"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/logging"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -89,20 +90,28 @@ type Local struct {
// opLock locks operations
opLock sync.Mutex
encryption encryption.StateEncryption
}
var _ backend.Backend = (*Local)(nil)
// New returns a new initialized local backend.
func New() *Local {
return NewWithBackend(nil)
func New(enc encryption.StateEncryption) *Local {
return &Local{
encryption: enc,
}
}
// NewWithBackend returns a new local backend initialized with a
// dedicated backend for non-enhanced behavior.
func NewWithBackend(backend backend.Backend) *Local {
func NewWithBackend(backend backend.Backend, enc encryption.StateEncryption) *Local {
if backend == nil && enc == nil {
panic("either backend or encryption required for backend.Local initialization")
}
return &Local{
Backend: backend,
Backend: backend,
encryption: enc,
}
}
@ -257,7 +266,7 @@ func (b *Local) StateMgr(name string) (statemgr.Full, error) {
statePath, stateOutPath, backupPath := b.StatePaths(name)
log.Printf("[TRACE] backend/local: state manager for workspace %q will:\n - read initial snapshot from %s\n - write new snapshots to %s\n - create any backup at %s", name, statePath, stateOutPath, backupPath)
s := statemgr.NewFilesystemBetweenPaths(statePath, stateOutPath)
s := statemgr.NewFilesystemBetweenPaths(statePath, stateOutPath, b.encryption)
if backupPath != "" {
s.SetBackupPath(backupPath)
}

View File

@ -295,7 +295,7 @@ func (b *Local) backupStateForError(stateFile *statefile.File, err error, view v
fmt.Sprintf("Error saving state: %s", err),
))
local := statemgr.NewFilesystem("errored.tfstate")
local := statemgr.NewFilesystem("errored.tfstate", b.encryption)
writeErr := local.WriteStateForMigration(stateFile, true)
if writeErr != nil {
diags = diags.Append(tfdiags.Sourceless(
@ -309,7 +309,7 @@ func (b *Local) backupStateForError(stateFile *statefile.File, err error, view v
// UX, so we should definitely avoid doing this if at all possible,
// but at least the user has _some_ path to recover if we end up
// here for some reason.
if dumpErr := view.EmergencyDumpState(stateFile); dumpErr != nil {
if dumpErr := view.EmergencyDumpState(stateFile, b.encryption); dumpErr != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,
"Failed to serialize state",

View File

@ -23,6 +23,7 @@ import (
"github.com/opentofu/opentofu/internal/command/views"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/depsfile"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/initwd"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/providers"
@ -351,7 +352,7 @@ type backendWithFailingState struct {
func (b *backendWithFailingState) StateMgr(name string) (statemgr.Full, error) {
return &failingState{
statemgr.NewFilesystem("failing-state.tfstate"),
statemgr.NewFilesystem("failing-state.tfstate", encryption.StateEncryptionDisabled()),
}, nil
}

View File

@ -136,7 +136,7 @@ func TestLocalRun_stalePlan(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error creating state file %s: %s", b.StatePath, err)
}
if err := statefile.Write(statefile.New(states.NewState(), "boop", 3), sf); err != nil {
if err := statefile.Write(statefile.New(states.NewState(), "boop", 3), sf, encryption.StateEncryptionDisabled()); err != nil {
t.Fatalf("unexpected error writing state file: %s", err)
}

View File

@ -14,19 +14,20 @@ import (
"testing"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
)
func TestLocal_impl(t *testing.T) {
var _ backend.Enhanced = New()
var _ backend.Local = New()
var _ backend.CLI = New()
var _ backend.Enhanced = New(encryption.StateEncryptionDisabled())
var _ backend.Local = New(encryption.StateEncryptionDisabled())
var _ backend.CLI = New(encryption.StateEncryptionDisabled())
}
func TestLocal_backend(t *testing.T) {
testTmpDir(t)
b := New()
b := New(encryption.StateEncryptionDisabled())
backend.TestBackendStates(t, b)
backend.TestBackendStateLocks(t, b, b)
}
@ -39,7 +40,7 @@ func checkState(t *testing.T, path, expected string) {
t.Fatalf("err: %s", err)
}
state, err := statefile.Read(f)
state, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -53,7 +54,7 @@ func checkState(t *testing.T, path, expected string) {
}
func TestLocal_StatePaths(t *testing.T) {
b := New()
b := New(encryption.StateEncryptionDisabled())
// Test the defaults
path, out, back := b.StatePaths("")
@ -98,7 +99,7 @@ func TestLocal_addAndRemoveStates(t *testing.T) {
dflt := backend.DefaultStateName
expectedStates := []string{dflt}
b := New()
b := New(encryption.StateEncryptionDisabled())
states, err := b.Workspaces()
if err != nil {
t.Fatal(err)
@ -190,7 +191,7 @@ func (b *testDelegateBackend) StateMgr(name string) (statemgr.Full, error) {
if b.stateErr {
return nil, errTestDelegateState
}
s := statemgr.NewFilesystem("terraform.tfstate")
s := statemgr.NewFilesystem("terraform.tfstate", encryption.StateEncryptionDisabled())
return s, nil
}
@ -215,7 +216,7 @@ func TestLocal_multiStateBackend(t *testing.T) {
stateErr: true,
statesErr: true,
deleteErr: true,
})
}, nil)
if _, err := b.StateMgr("test"); err != errTestDelegateState {
t.Fatal("expected errTestDelegateState, got:", err)

View File

@ -14,6 +14,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -32,7 +33,7 @@ func TestLocal(t *testing.T) *Local {
t.Fatal(err)
}
local := New()
local := New(encryption.StateEncryptionDisabled())
local.StatePath = filepath.Join(tempDir, "state.tfstate")
local.StateOutPath = filepath.Join(tempDir, "state.tfstate")
local.StateBackupPath = filepath.Join(tempDir, "state.tfstate.bak")
@ -115,8 +116,8 @@ type TestLocalSingleState struct {
// TestNewLocalSingle is a factory for creating a TestLocalSingleState.
// This function matches the signature required for backend/init.
func TestNewLocalSingle() backend.Backend {
return &TestLocalSingleState{Local: New()}
func TestNewLocalSingle(enc encryption.StateEncryption) backend.Backend {
return &TestLocalSingleState{Local: New(encryption.StateEncryptionDisabled())}
}
func (b *TestLocalSingleState) Workspaces() ([]string, error) {
@ -145,8 +146,8 @@ type TestLocalNoDefaultState struct {
// TestNewLocalNoDefault is a factory for creating a TestLocalNoDefaultState.
// This function matches the signature required for backend/init.
func TestNewLocalNoDefault() backend.Backend {
return &TestLocalNoDefaultState{Local: New()}
func TestNewLocalNoDefault(enc encryption.StateEncryption) backend.Backend {
return &TestLocalNoDefaultState{Local: New(encryption.StateEncryptionDisabled())}
}
func (b *TestLocalNoDefaultState) Workspaces() ([]string, error) {
@ -182,7 +183,7 @@ func (b *TestLocalNoDefaultState) StateMgr(name string) (statemgr.Full, error) {
func testStateFile(t *testing.T, path string, s *states.State) {
t.Helper()
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem(path), s, nil); err != nil {
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem(path, encryption.StateEncryptionDisabled()), s, nil); err != nil {
t.Fatal(err)
}
}

View File

@ -10,11 +10,12 @@ import (
"fmt"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
)
// New creates a new backend for Azure remote state.
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"storage_account_name": {
@ -182,13 +183,14 @@ func New() backend.Backend {
},
}
result := &Backend{Backend: s}
result := &Backend{Backend: s, encryption: enc}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
// The fields below are set from configure
armClient *ArmClient

View File

@ -98,7 +98,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
snapshot: b.snapshot,
}
stateMgr := &remote.State{Client: client}
stateMgr := remote.NewState(client, b.encryption)
// Grab the value
if err := stateMgr.RefreshState(); err != nil {

View File

@ -11,6 +11,7 @@ import (
"testing"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/acctest"
)
@ -31,7 +32,7 @@ func TestBackendConfig(t *testing.T) {
"access_key": "QUNDRVNTX0tFWQ0K",
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend)
if b.containerName != "tfcontainer" {
t.Fatalf("Incorrect bucketName was populated")
@ -58,7 +59,7 @@ func TestAccBackendAccessKeyBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -88,7 +89,7 @@ func TestAccBackendSASTokenBasic(t *testing.T) {
t.Fatalf("Error building SAS Token: %+v", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -113,7 +114,7 @@ func TestAccBackendOIDCBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -141,7 +142,7 @@ func TestAccBackendManagedServiceIdentityBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -176,7 +177,7 @@ func TestAccBackendServicePrincipalClientCertificateBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -206,7 +207,7 @@ func TestAccBackendServicePrincipalClientSecretBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -242,7 +243,7 @@ func TestAccBackendServicePrincipalClientSecretCustomEndpoint(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -271,7 +272,7 @@ func TestAccBackendAccessKeyLocked(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -280,7 +281,7 @@ func TestAccBackendAccessKeyLocked(t *testing.T) {
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -309,7 +310,7 @@ func TestAccBackendServicePrincipalLocked(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -322,7 +323,7 @@ func TestAccBackendServicePrincipalLocked(t *testing.T) {
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,

View File

@ -11,6 +11,7 @@ import (
"testing"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/acctest"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/tombuildsstuff/giovanni/storage/2018-11-09/blob/blobs"
@ -34,7 +35,7 @@ func TestRemoteClientAccessKeyBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -64,7 +65,7 @@ func TestRemoteClientManagedServiceIdentityBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -102,7 +103,7 @@ func TestRemoteClientSasTokenBasic(t *testing.T) {
t.Fatalf("Error building SAS Token: %+v", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -132,7 +133,7 @@ func TestRemoteClientServicePrincipalBasic(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -166,7 +167,7 @@ func TestRemoteClientAccessKeyLocks(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -175,7 +176,7 @@ func TestRemoteClientAccessKeyLocks(t *testing.T) {
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -210,7 +211,7 @@ func TestRemoteClientServicePrincipalLocks(t *testing.T) {
t.Fatalf("Error creating Test Resources: %q", err)
}
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,
@ -223,7 +224,7 @@ func TestRemoteClientServicePrincipalLocks(t *testing.T) {
"endpoint": os.Getenv("ARM_ENDPOINT"),
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"storage_account_name": res.storageAccountName,
"container_name": res.storageContainerName,
"key": res.storageKeyName,

View File

@ -13,11 +13,12 @@ import (
consulapi "github.com/hashicorp/consul/api"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
)
// New creates a new backend for Consul remote state.
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"path": &schema.Schema{
@ -98,13 +99,14 @@ func New() backend.Backend {
},
}
result := &Backend{Backend: s}
result := &Backend{Backend: s, encryption: enc}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
// The fields below are set from configure
client *consulapi.Client

View File

@ -76,14 +76,15 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
gzip := b.configData.Get("gzip").(bool)
// Build the state client
var stateMgr = &remote.State{
Client: &RemoteClient{
var stateMgr = remote.NewState(
&RemoteClient{
Client: b.client,
Path: path,
GZip: gzip,
lockState: b.lock,
},
}
b.encryption,
)
if !b.lock {
stateMgr.DisableLocks()

View File

@ -15,6 +15,7 @@ import (
"github.com/hashicorp/consul/sdk/testutil"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
)
func TestBackend_impl(t *testing.T) {
@ -56,12 +57,12 @@ func TestBackend(t *testing.T) {
path := fmt.Sprintf("tf-unit/%s", time.Now().String())
// Get the backend. We need two to test locking.
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))
@ -78,13 +79,13 @@ func TestBackend_lockDisabled(t *testing.T) {
path := fmt.Sprintf("tf-unit/%s", time.Now().String())
// Get the backend. We need two to test locking.
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
"lock": false,
}))
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path + "different", // Diff so locking test would fail if it was locking
"lock": false,
@ -100,7 +101,7 @@ func TestBackend_gzip(t *testing.T) {
defer func() { _ = srv.Stop() }()
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": fmt.Sprintf("tf-unit/%s", time.Now().String()),
"gzip": true,

View File

@ -19,6 +19,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statemgr"
)
@ -40,7 +41,7 @@ func TestRemoteClient(t *testing.T) {
for _, path := range testCases {
t.Run(path, func(*testing.T) {
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))
@ -65,7 +66,7 @@ func TestRemoteClient_gzipUpgrade(t *testing.T) {
statePath := fmt.Sprintf("tf-unit/%s", time.Now().String())
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": statePath,
}))
@ -80,7 +81,7 @@ func TestRemoteClient_gzipUpgrade(t *testing.T) {
remote.TestClient(t, state.(*remote.State).Client)
// create a new backend with gzip
b = backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b = backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": statePath,
"gzip": true,
@ -105,7 +106,7 @@ func TestConsul_largeState(t *testing.T) {
path := "tf-unit/test-large-state"
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))
@ -191,7 +192,7 @@ func TestConsul_largeState(t *testing.T) {
)
// Test with gzip and chunks
b = backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b = backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
"gzip": true,
@ -253,7 +254,7 @@ func TestConsul_stateLock(t *testing.T) {
for _, path := range testCases {
t.Run(path, func(*testing.T) {
// create 2 instances to get 2 remote.Clients
sA, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
sA, err := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
})).StateMgr(backend.DefaultStateName)
@ -261,7 +262,7 @@ func TestConsul_stateLock(t *testing.T) {
t.Fatal(err)
}
sB, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
sB, err := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
})).StateMgr(backend.DefaultStateName)
@ -297,7 +298,7 @@ func TestConsul_destroyLock(t *testing.T) {
for _, path := range testCases {
t.Run(path, func(*testing.T) {
// Get the backend
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))
@ -364,7 +365,7 @@ func TestConsul_lostLock(t *testing.T) {
path := fmt.Sprintf("tf-unit/%s", time.Now().String())
// create 2 instances to get 2 remote.Clients
sA, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
sA, err := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
})).StateMgr(backend.DefaultStateName)
@ -372,7 +373,7 @@ func TestConsul_lostLock(t *testing.T) {
t.Fatal(err)
}
sB, err := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
sB, err := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path + "-not-used",
})).StateMgr(backend.DefaultStateName)
@ -422,7 +423,7 @@ func TestConsul_lostLockConnection(t *testing.T) {
path := fmt.Sprintf("tf-unit/%s", time.Now().String())
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"address": srv.HTTPAddr,
"path": path,
}))

View File

@ -16,6 +16,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
@ -38,6 +39,7 @@ const (
// Backend implements "backend".Backend for tencentCloud cos
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
credential *common.Credential
cosContext context.Context
@ -54,7 +56,7 @@ type Backend struct {
}
// New creates a new backend for TencentCloud cos remote state.
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"secret_id": {
@ -182,7 +184,7 @@ func New() backend.Backend {
},
}
result := &Backend{Backend: s}
result := &Backend{Backend: s, encryption: enc}
result.Backend.ConfigureFunc = result.configure
return result

View File

@ -85,7 +85,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
if err != nil {
return nil, err
}
stateMgr := &remote.State{Client: c}
stateMgr := remote.NewState(c, b.encryption)
ws, err := b.Workspaces()
if err != nil {

View File

@ -13,6 +13,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
)
@ -225,7 +226,7 @@ func setupBackend(t *testing.T, bucket, prefix, key string, encrypt bool) backen
"key": key,
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config))
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config))
be := b.(*Backend)
c, err := be.client("tencentcloud")

View File

@ -16,6 +16,7 @@ import (
"cloud.google.com/go/storage"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
"github.com/opentofu/opentofu/version"
@ -29,6 +30,7 @@ import (
// State(), DeleteState() and States() are implemented explicitly.
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
storageClient *storage.Client
storageContext context.Context
@ -40,8 +42,8 @@ type Backend struct {
kmsKeyName string
}
func New() backend.Backend {
b := &Backend{}
func New(enc encryption.StateEncryption) backend.Backend {
b := &Backend{encryption: enc}
b.Backend = &schema.Backend{
ConfigureFunc: b.configure,
Schema: map[string]*schema.Schema{

View File

@ -98,7 +98,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
return nil, err
}
st := &remote.State{Client: c}
st := remote.NewState(c, b.encryption)
// Grab the value
if err := st.RefreshState(); err != nil {

View File

@ -18,6 +18,7 @@ import (
kms "cloud.google.com/go/kms/apiv1"
"cloud.google.com/go/storage"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/version"
@ -238,7 +239,7 @@ func setupBackend(t *testing.T, bucket, prefix, key, kmsName string) backend.Bac
config["kms_encryption_key"] = kmsName
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config))
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config))
be := b.(*Backend)
// create the bucket if it doesn't exist

View File

@ -19,13 +19,14 @@ import (
"github.com/hashicorp/go-retryablehttp"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
"github.com/opentofu/opentofu/internal/logging"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statemgr"
)
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"address": &schema.Schema{
@ -121,13 +122,14 @@ func New() backend.Backend {
},
}
b := &Backend{Backend: s}
b := &Backend{Backend: s, encryption: enc}
b.Backend.ConfigureFunc = b.configure
return b
}
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
client *httpClient
}
@ -250,7 +252,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
return nil, backend.ErrWorkspacesNotSupported
}
return &remote.State{Client: b.client}, nil
return remote.NewState(b.client, b.encryption), nil
}
func (b *Backend) Workspaces() ([]string, error) {

View File

@ -10,6 +10,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/backend"
@ -25,7 +26,7 @@ func TestHTTPClientFactory(t *testing.T) {
conf := map[string]cty.Value{
"address": cty.StringVal("http://127.0.0.1:8888/foo"),
}
b := backend.TestBackendConfig(t, New(), configs.SynthBody("synth", conf)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), configs.SynthBody("synth", conf)).(*Backend)
client := b.client
if client == nil {
@ -62,7 +63,7 @@ func TestHTTPClientFactory(t *testing.T) {
"retry_wait_max": cty.StringVal("150"),
}
b = backend.TestBackendConfig(t, New(), configs.SynthBody("synth", conf)).(*Backend)
b = backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), configs.SynthBody("synth", conf)).(*Backend)
client = b.client
if client == nil {
@ -122,7 +123,7 @@ func TestHTTPClientFactoryWithEnv(t *testing.T) {
t.Setenv("TF_HTTP_RETRY_WAIT_MIN", conf["retry_wait_min"])
t.Setenv("TF_HTTP_RETRY_WAIT_MAX", conf["retry_wait_max"])
b := backend.TestBackendConfig(t, New(), nil).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), nil).(*Backend)
client := b.client
if client == nil {

View File

@ -31,6 +31,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/zclconf/go-cty/cty"
)
@ -276,7 +277,7 @@ func TestMTLSServer_NoCertFails(t *testing.T) {
"address": cty.StringVal(url),
"skip_cert_verification": cty.BoolVal(true),
}
b := backend.TestBackendConfig(t, New(), configs.SynthBody("synth", conf)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), configs.SynthBody("synth", conf)).(*Backend)
if nil == b {
t.Fatal("nil backend")
}
@ -347,7 +348,7 @@ func TestMTLSServer_WithCertPasses(t *testing.T) {
"client_certificate_pem": cty.StringVal(string(clientCertData)),
"client_private_key_pem": cty.StringVal(string(clientKeyData)),
}
b := backend.TestBackendConfig(t, New(), configs.SynthBody("synth", conf)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), configs.SynthBody("synth", conf)).(*Backend)
if nil == b {
t.Fatal("nil backend")
}

View File

@ -14,6 +14,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
statespkg "github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/remote"
@ -46,7 +47,7 @@ func Reset() {
}
// New creates a new backend for Inmem remote state.
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
// Set the schema
s := &schema.Backend{
Schema: map[string]*schema.Schema{
@ -57,13 +58,14 @@ func New() backend.Backend {
},
},
}
backend := &Backend{Backend: s}
backend := &Backend{Backend: s, encryption: enc}
backend.Backend.ConfigureFunc = backend.configure
return backend
}
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
}
func (b *Backend) configure(ctx context.Context) error {
@ -124,11 +126,12 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
s := states.m[name]
if s == nil {
s = &remote.State{
Client: &RemoteClient{
s = remote.NewState(
&RemoteClient{
Name: name,
},
}
b.encryption,
)
states.m[name] = s
// to most closely replicate other implementations, we are going to

View File

@ -13,6 +13,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
statespkg "github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/remote"
@ -36,7 +37,7 @@ func TestBackendConfig(t *testing.T) {
"lock_id": testID,
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend)
s, err := b.StateMgr(backend.DefaultStateName)
if err != nil {
@ -55,14 +56,14 @@ func TestBackendConfig(t *testing.T) {
func TestBackend(t *testing.T) {
defer Reset()
b := backend.TestBackendConfig(t, New(), hcl.EmptyBody()).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), hcl.EmptyBody()).(*Backend)
backend.TestBackendStates(t, b)
}
func TestBackendLocked(t *testing.T) {
defer Reset()
b1 := backend.TestBackendConfig(t, New(), hcl.EmptyBody()).(*Backend)
b2 := backend.TestBackendConfig(t, New(), hcl.EmptyBody()).(*Backend)
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), hcl.EmptyBody()).(*Backend)
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), hcl.EmptyBody()).(*Backend)
backend.TestBackendStateLocks(t, b1, b2)
}
@ -70,7 +71,7 @@ func TestBackendLocked(t *testing.T) {
// use the this backen to test the remote.State implementation
func TestRemoteState(t *testing.T) {
defer Reset()
b := backend.TestBackendConfig(t, New(), hcl.EmptyBody())
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), hcl.EmptyBody())
workspace := "workspace"

View File

@ -10,6 +10,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
)
@ -20,7 +21,7 @@ func TestRemoteClient_impl(t *testing.T) {
func TestRemoteClient(t *testing.T) {
defer Reset()
b := backend.TestBackendConfig(t, New(), hcl.EmptyBody())
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), hcl.EmptyBody())
s, err := b.StateMgr(backend.DefaultStateName)
if err != nil {
@ -32,7 +33,7 @@ func TestRemoteClient(t *testing.T) {
func TestInmemLocks(t *testing.T) {
defer Reset()
s, err := backend.TestBackendConfig(t, New(), hcl.EmptyBody()).StateMgr(backend.DefaultStateName)
s, err := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), hcl.EmptyBody()).StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
}

View File

@ -15,6 +15,7 @@ import (
"github.com/mitchellh/go-homedir"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
"github.com/opentofu/opentofu/version"
@ -36,7 +37,7 @@ var (
)
// New creates a new backend for kubernetes remote state.
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"secret_suffix": {
@ -176,13 +177,14 @@ func New() backend.Backend {
},
}
result := &Backend{Backend: s}
result := &Backend{Backend: s, encryption: enc}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
// The fields below are set from configure
kubernetesSecretClient dynamic.ResourceInterface

View File

@ -84,7 +84,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
return nil, err
}
stateMgr := &remote.State{Client: c}
stateMgr := remote.NewState(c, b.encryption)
// Grab the value
if err := stateMgr.RefreshState(); err != nil {

View File

@ -15,6 +15,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statemgr"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -52,7 +53,7 @@ func TestBackend(t *testing.T) {
testACC(t)
defer cleanupK8sResources(t)
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
@ -65,11 +66,11 @@ func TestBackendLocks(t *testing.T) {
defer cleanupK8sResources(t)
// Get the backend. We need two to test locking.
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
@ -87,7 +88,7 @@ func TestBackendLocksSoak(t *testing.T) {
lockers := []statemgr.Locker{}
for i := 0; i < clientCount; i++ {
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
@ -132,7 +133,7 @@ func TestBackendLocksSoak(t *testing.T) {
func cleanupK8sResources(t *testing.T) {
ctx := context.Background()
// Get a backend to use the k8s client
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))

View File

@ -9,6 +9,7 @@ import (
"testing"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statemgr"
)
@ -22,7 +23,7 @@ func TestRemoteClient(t *testing.T) {
testACC(t)
defer cleanupK8sResources(t)
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
@ -38,11 +39,11 @@ func TestRemoteClientLocks(t *testing.T) {
testACC(t)
defer cleanupK8sResources(t)
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
@ -63,11 +64,11 @@ func TestForceUnlock(t *testing.T) {
testACC(t)
defer cleanupK8sResources(t)
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"secret_suffix": secretSuffix,
}))

View File

@ -34,6 +34,7 @@ import (
"github.com/mitchellh/go-homedir"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
"github.com/opentofu/opentofu/version"
@ -90,7 +91,7 @@ func deprecatedAssumeRoleSchema() *schema.Schema {
}
// New creates a new backend for OSS remote state.
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"access_key": {
@ -262,13 +263,14 @@ func New() backend.Backend {
},
}
result := &Backend{Backend: s}
result := &Backend{Backend: s, encryption: enc}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
// The fields below are set from configure
ossClient *oss.Client

View File

@ -118,7 +118,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
if err != nil {
return nil, err
}
stateMgr := &remote.State{Client: client}
stateMgr := remote.NewState(client, b.encryption)
// Check to see if this state already exists.
existing, err := b.Workspaces()

View File

@ -18,6 +18,7 @@ import (
"github.com/aliyun/aliyun-tablestore-go-sdk/tablestore"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
"github.com/opentofu/opentofu/internal/encryption"
)
// verify that we are doing ACC tests or the OSS tests specifically
@ -49,7 +50,7 @@ func TestBackendConfig(t *testing.T) {
"tablestore_table": "TableStore",
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend)
if !strings.HasPrefix(b.ossClient.Config.Endpoint, "https://oss-cn-beijing") {
t.Fatalf("Incorrect region was provided")
@ -84,7 +85,7 @@ func TestBackendConfigWorkSpace(t *testing.T) {
"tablestore_table": "TableStore",
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend)
createOSSBucket(t, b.ossClient, bucketName)
defer deleteOSSBucket(t, b.ossClient, bucketName)
if _, err := b.Workspaces(); err != nil {
@ -123,7 +124,7 @@ func TestBackendConfigProfile(t *testing.T) {
"profile": "default",
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend)
if !strings.HasPrefix(b.ossClient.Config.Endpoint, "https://oss-cn-beijing") {
t.Fatalf("Incorrect region was provided")
@ -157,7 +158,7 @@ func TestBackendConfig_invalidKey(t *testing.T) {
"tablestore_table": "TableStore",
})
_, results := New().PrepareConfig(cfg)
_, results := New(encryption.StateEncryptionDisabled()).PrepareConfig(cfg)
if !results.HasErrors() {
t.Fatal("expected config validation error")
}
@ -169,12 +170,12 @@ func TestBackend(t *testing.T) {
bucketName := fmt.Sprintf("terraform-remote-oss-test-%x", time.Now().Unix())
statePrefix := "multi/level/path/"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": statePrefix,
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": statePrefix,
})).(*Backend)

View File

@ -15,6 +15,7 @@ import (
"crypto/md5"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -33,7 +34,7 @@ func TestRemoteClient(t *testing.T) {
bucketName := fmt.Sprintf("tf-remote-oss-test-%x", time.Now().Unix())
path := "testState"
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"encrypt": true,
@ -56,7 +57,7 @@ func TestRemoteClientLocks(t *testing.T) {
tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix())
path := "testState"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"encrypt": true,
@ -64,7 +65,7 @@ func TestRemoteClientLocks(t *testing.T) {
"tablestore_endpoint": RemoteTestUsedOTSEndpoint,
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"encrypt": true,
@ -97,7 +98,7 @@ func TestRemoteClientLocks_multipleStates(t *testing.T) {
tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix())
path := "testState"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"encrypt": true,
@ -105,7 +106,7 @@ func TestRemoteClientLocks_multipleStates(t *testing.T) {
"tablestore_endpoint": RemoteTestUsedOTSEndpoint,
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"encrypt": true,
@ -143,7 +144,7 @@ func TestRemoteForceUnlock(t *testing.T) {
tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix())
path := "testState"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"encrypt": true,
@ -151,7 +152,7 @@ func TestRemoteForceUnlock(t *testing.T) {
"tablestore_endpoint": RemoteTestUsedOTSEndpoint,
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"encrypt": true,
@ -223,7 +224,7 @@ func TestRemoteClient_clientMD5(t *testing.T) {
tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix())
path := "testState"
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"tablestore_table": tableName,
@ -273,7 +274,7 @@ func TestRemoteClient_stateChecksum(t *testing.T) {
tableName := fmt.Sprintf("tfRemoteTestForce%x", time.Now().Unix())
path := "testState"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
"tablestore_table": tableName,
@ -295,18 +296,18 @@ func TestRemoteClient_stateChecksum(t *testing.T) {
s := statemgr.TestFullInitialState()
sf := &statefile.File{State: s}
var oldState bytes.Buffer
if err := statefile.Write(sf, &oldState); err != nil {
if err := statefile.Write(sf, &oldState, encryption.StateEncryptionDisabled()); err != nil {
t.Fatal(err)
}
sf.Serial++
var newState bytes.Buffer
if err := statefile.Write(sf, &newState); err != nil {
if err := statefile.Write(sf, &newState, encryption.StateEncryptionDisabled()); err != nil {
t.Fatal(err)
}
// Use b2 without a tablestore_table to bypass the lock table to write the state directly.
// client2 will write the "incorrect" state, simulating oss eventually consistency delays
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"prefix": path,
})).(*Backend)

View File

@ -14,6 +14,7 @@ import (
"github.com/lib/pq"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/legacy/helper/schema"
)
@ -33,7 +34,7 @@ func defaultBoolFunc(k string, dv bool) schema.SchemaDefaultFunc {
}
// New creates a new backend for Postgres remote state.
func New() backend.Backend {
func New(enc encryption.StateEncryption) backend.Backend {
s := &schema.Backend{
Schema: map[string]*schema.Schema{
"conn_str": {
@ -73,13 +74,14 @@ func New() backend.Backend {
},
}
result := &Backend{Backend: s}
result := &Backend{Backend: s, encryption: enc}
result.Backend.ConfigureFunc = result.configure
return result
}
type Backend struct {
*schema.Backend
encryption encryption.StateEncryption
// The fields below are set from configure
db *sql.DB

View File

@ -56,13 +56,14 @@ func (b *Backend) DeleteWorkspace(name string, _ bool) error {
func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
// Build the state client
var stateMgr statemgr.Full = &remote.State{
Client: &RemoteClient{
var stateMgr statemgr.Full = remote.NewState(
&RemoteClient{
Client: b.db,
Name: name,
SchemaName: b.schemaName,
},
}
b.encryption,
)
// Check to see if this state already exists.
// If the state doesn't exist, we have to assume this

View File

@ -19,6 +19,7 @@ import (
"github.com/hashicorp/hcl/v2/hcldec"
"github.com/lib/pq"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -162,7 +163,7 @@ func TestBackendConfig(t *testing.T) {
defer dbCleaner.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName))
var diags tfdiags.Diagnostics
b := New().(*Backend)
b := New(encryption.StateEncryptionDisabled()).(*Backend)
schema := b.ConfigSchema()
spec := schema.DecoderSpec()
obj, decDiags := hcldec.Decode(config, spec, nil)
@ -325,7 +326,7 @@ func TestBackendConfigSkipOptions(t *testing.T) {
}
defer db.Query(fmt.Sprintf("DROP SCHEMA IF EXISTS %s CASCADE", schemaName))
b := backend.TestBackendConfig(t, New(), config).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
if b == nil {
t.Fatal("Backend could not be configured")
@ -398,7 +399,7 @@ func TestBackendStates(t *testing.T) {
"conn_str": connStr,
"schema_name": schemaName,
})
b := backend.TestBackendConfig(t, New(), config).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
if b == nil {
t.Fatal("Backend could not be configured")
@ -423,13 +424,13 @@ func TestBackendStateLocks(t *testing.T) {
"conn_str": connStr,
"schema_name": schemaName,
})
b := backend.TestBackendConfig(t, New(), config).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
if b == nil {
t.Fatal("Backend could not be configured")
}
bb := backend.TestBackendConfig(t, New(), config).(*Backend)
bb := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
if bb == nil {
t.Fatal("Backend could not be configured")
@ -452,7 +453,7 @@ func TestBackendConcurrentLock(t *testing.T) {
"conn_str": connStr,
"schema_name": schemaName,
})
b := backend.TestBackendConfig(t, New(), config).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
if b == nil {
t.Fatal("Backend could not be configured")

View File

@ -14,6 +14,7 @@ import (
"testing"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
)
@ -36,7 +37,7 @@ func TestRemoteClient(t *testing.T) {
"conn_str": connStr,
"schema_name": schemaName,
})
b := backend.TestBackendConfig(t, New(), config).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
if b == nil {
t.Fatal("Backend could not be configured")
@ -65,13 +66,13 @@ func TestRemoteLocks(t *testing.T) {
"schema_name": schemaName,
})
b1 := backend.TestBackendConfig(t, New(), config).(*Backend)
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
s1, err := b1.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)
}
b2 := backend.TestBackendConfig(t, New(), config).(*Backend)
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), config).(*Backend)
s2, err := b2.StateMgr(backend.DefaultStateName)
if err != nil {
t.Fatal(err)

View File

@ -25,6 +25,7 @@ import (
awsbaseValidation "github.com/hashicorp/aws-sdk-go-base/v2/validation"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/logging"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -33,14 +34,15 @@ import (
"github.com/zclconf/go-cty/cty/gocty"
)
func New() backend.Backend {
return &Backend{}
func New(enc encryption.StateEncryption) backend.Backend {
return &Backend{encryption: enc}
}
type Backend struct {
s3Client *s3.Client
dynClient *dynamodb.Client
awsConfig aws.Config
encryption encryption.StateEncryption
s3Client *s3.Client
dynClient *dynamodb.Client
awsConfig aws.Config
bucketName string
keyName string

View File

@ -19,6 +19,7 @@ import (
"github.com/hashicorp/aws-sdk-go-base/v2/mockdata"
"github.com/hashicorp/aws-sdk-go-base/v2/servicemocks"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/tfdiags"
)
@ -2206,7 +2207,7 @@ func setSharedConfigFile(t *testing.T, filename string) {
}
func configureBackend(t *testing.T, config map[string]any) (*Backend, tfdiags.Diagnostics) {
b := New().(*Backend)
b := New(encryption.StateEncryptionDisabled()).(*Backend)
configSchema := populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config))
configSchema, diags := b.PrepareConfig(configSchema)

View File

@ -148,7 +148,7 @@ func (b *Backend) StateMgr(name string) (statemgr.Full, error) {
return nil, err
}
stateMgr := &remote.State{Client: client}
stateMgr := remote.NewState(client, b.encryption)
// Check to see if this state already exists.
// If we're trying to force-unlock a state, we can't take the lock before
// fetching the state. If the state doesn't exist, we have to assume this

View File

@ -31,6 +31,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -70,7 +71,7 @@ func TestBackendConfig_original(t *testing.T) {
"dynamodb_table": "dynamoTable",
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend)
if b.awsConfig.Region != "us-west-1" {
t.Fatalf("Incorrect region was populated")
@ -134,7 +135,7 @@ func TestBackendConfig_InvalidRegion(t *testing.T) {
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
b := New()
b := New(encryption.StateEncryptionDisabled())
configSchema := populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(tc.config))
configSchema, diags := b.PrepareConfig(configSchema)
@ -181,7 +182,7 @@ func TestBackendConfig_RegionEnvVar(t *testing.T) {
t.Setenv(k, v)
}
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config)).(*Backend)
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config)).(*Backend)
if b.awsConfig.Region != "us-west-1" {
t.Fatalf("Incorrect region was populated")
@ -235,7 +236,7 @@ func TestBackendConfig_DynamoDBEndpoint(t *testing.T) {
}
}
backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config))
backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config))
})
}
}
@ -285,7 +286,7 @@ func TestBackendConfig_S3Endpoint(t *testing.T) {
}
}
backend.TestBackendConfig(t, New(), backend.TestWrapConfig(config))
backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(config))
})
}
}
@ -355,7 +356,7 @@ func TestBackendConfig_STSEndpoint(t *testing.T) {
config["sts_endpoint"] = endpoint
}
b := New()
b := New(encryption.StateEncryptionDisabled())
configSchema := populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config))
configSchema, diags := b.PrepareConfig(configSchema)
@ -593,7 +594,7 @@ func TestBackendConfig_AssumeRole(t *testing.T) {
testCase.Config["sts_endpoint"] = endpoint
b := New()
b := New(encryption.StateEncryptionDisabled())
diags := b.Configure(populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(testCase.Config)))
if diags.HasErrors() {
@ -776,7 +777,7 @@ func TestBackendConfig_PrepareConfigValidation(t *testing.T) {
t.Run(name, func(t *testing.T) {
servicemocks.StashEnv(t)
b := New()
b := New(encryption.StateEncryptionDisabled())
_, valDiags := b.PrepareConfig(populateSchema(t, b.ConfigSchema(), tc.config))
if tc.expectedErr != "" {
@ -815,7 +816,7 @@ func TestBackendConfig_PrepareConfigValidationWarnings(t *testing.T) {
t.Run(name, func(t *testing.T) {
servicemocks.StashEnv(t)
b := New()
b := New(encryption.StateEncryptionDisabled())
_, diags := b.PrepareConfig(populateSchema(t, b.ConfigSchema(), tc.config))
if tc.expectedWarn != "" {
@ -878,7 +879,7 @@ func TestBackendConfig_PrepareConfigWithEnvVars(t *testing.T) {
t.Run(name, func(t *testing.T) {
servicemocks.StashEnv(t)
b := New()
b := New(encryption.StateEncryptionDisabled())
for k, v := range tc.vars {
t.Setenv(k, v)
@ -1043,7 +1044,7 @@ func TestBackendConfig_proxy(t *testing.T) {
t.Setenv(k, v)
}
b := New()
b := New(encryption.StateEncryptionDisabled())
got := b.Configure(populateSchema(t, b.ConfigSchema(), tc.config))
if got.HasErrors() != (tc.wantErrSubstr != "") {
@ -1077,7 +1078,7 @@ func TestBackend(t *testing.T) {
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
keyName := "testState"
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
@ -1097,7 +1098,7 @@ func TestBackendLocked(t *testing.T) {
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
keyName := "test/state"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
@ -1105,7 +1106,7 @@ func TestBackendLocked(t *testing.T) {
"region": "us-west-1",
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
@ -1156,7 +1157,7 @@ func TestBackendSSECustomerKeyConfig(t *testing.T) {
"region": "us-west-1",
}
b := New().(*Backend)
b := New(encryption.StateEncryptionDisabled()).(*Backend)
diags := b.Configure(populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config)))
if testCase.expectedErr != "" {
@ -1220,7 +1221,7 @@ func TestBackendSSECustomerKeyEnvVar(t *testing.T) {
t.Setenv("AWS_SSE_CUSTOMER_KEY", testCase.customerKey)
b := New().(*Backend)
b := New(encryption.StateEncryptionDisabled()).(*Backend)
diags := b.Configure(populateSchema(t, b.ConfigSchema(), hcl2shim.HCL2ValueFromConfigValue(config)))
if testCase.expectedErr != "" {
@ -1256,7 +1257,7 @@ func TestBackendExtraPaths(t *testing.T) {
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
keyName := "test/state/tfstate"
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
@ -1395,7 +1396,7 @@ func TestBackendPrefixInWorkspace(t *testing.T) {
testACC(t)
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": "test-env.tfstate",
"workspace_key_prefix": "env",
@ -1424,7 +1425,7 @@ func TestKeyEnv(t *testing.T) {
keyName := "some/paths/tfstate"
bucket0Name := fmt.Sprintf("%s-%x-0", testBucketPrefix, time.Now().Unix())
b0 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b0 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucket0Name,
"key": keyName,
"encrypt": true,
@ -1436,7 +1437,7 @@ func TestKeyEnv(t *testing.T) {
defer deleteS3Bucket(ctx, t, b0.s3Client, bucket0Name)
bucket1Name := fmt.Sprintf("%s-%x-1", testBucketPrefix, time.Now().Unix())
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucket1Name,
"key": keyName,
"encrypt": true,
@ -1447,7 +1448,7 @@ func TestKeyEnv(t *testing.T) {
defer deleteS3Bucket(ctx, t, b1.s3Client, bucket1Name)
bucket2Name := fmt.Sprintf("%s-%x-2", testBucketPrefix, time.Now().Unix())
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucket2Name,
"key": keyName,
"encrypt": true,
@ -1577,7 +1578,7 @@ func TestBackend_schemaCoercionMinimal(t *testing.T) {
"bucket": cty.StringVal("my-bucket"),
"key": cty.StringVal("state.tf"),
})
schema := New().ConfigSchema()
schema := New(encryption.StateEncryptionDisabled()).ConfigSchema()
_, err := schema.CoerceValue(example)
if err != nil {
t.Errorf("Unexpected error: %s", err.Error())

View File

@ -15,6 +15,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -30,7 +31,7 @@ func TestRemoteClient(t *testing.T) {
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
keyName := "testState"
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
@ -53,14 +54,14 @@ func TestRemoteClientLocks(t *testing.T) {
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
keyName := "testState"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
"dynamodb_table": bucketName,
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
@ -92,14 +93,14 @@ func TestForceUnlock(t *testing.T) {
bucketName := fmt.Sprintf("%s-force-%x", testBucketPrefix, time.Now().Unix())
keyName := "testState"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
"dynamodb_table": bucketName,
})).(*Backend)
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"encrypt": true,
@ -170,7 +171,7 @@ func TestRemoteClient_clientMD5(t *testing.T) {
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
keyName := "testState"
b := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"dynamodb_table": bucketName,
@ -219,7 +220,7 @@ func TestRemoteClient_stateChecksum(t *testing.T) {
bucketName := fmt.Sprintf("%s-%x", testBucketPrefix, time.Now().Unix())
keyName := "testState"
b1 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b1 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
"dynamodb_table": bucketName,
@ -241,18 +242,18 @@ func TestRemoteClient_stateChecksum(t *testing.T) {
s := statemgr.TestFullInitialState()
sf := &statefile.File{State: s}
var oldState bytes.Buffer
if err := statefile.Write(sf, &oldState); err != nil {
if err := statefile.Write(sf, &oldState, encryption.StateEncryptionDisabled()); err != nil {
t.Fatal(err)
}
sf.Serial++
var newState bytes.Buffer
if err := statefile.Write(sf, &newState); err != nil {
if err := statefile.Write(sf, &newState, encryption.StateEncryptionDisabled()); err != nil {
t.Fatal(err)
}
// Use b2 without a dynamodb_table to bypass the lock table to write the state directly.
// client2 will write the "incorrect" state, simulating s3 eventually consistency delays
b2 := backend.TestBackendConfig(t, New(), backend.TestWrapConfig(map[string]interface{}{
b2 := backend.TestBackendConfig(t, New(encryption.StateEncryptionDisabled()), backend.TestWrapConfig(map[string]interface{}{
"bucket": bucketName,
"key": keyName,
})).(*Backend)

View File

@ -25,6 +25,7 @@ import (
"github.com/mitchellh/colorstring"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/logging"
"github.com/opentofu/opentofu/internal/states/remote"
@ -95,6 +96,8 @@ type Remote struct {
// version. This will also cause VerifyWorkspaceTerraformVersion to return
// a warning diagnostic instead of an error.
ignoreVersionConflict bool
encryption encryption.StateEncryption
}
var _ backend.Backend = (*Remote)(nil)
@ -102,9 +105,10 @@ var _ backend.Enhanced = (*Remote)(nil)
var _ backend.Local = (*Remote)(nil)
// New creates a new initialized remote backend.
func New(services *disco.Disco) *Remote {
func New(services *disco.Disco, enc encryption.StateEncryption) *Remote {
return &Remote{
services: services,
services: services,
encryption: enc,
}
}
@ -374,7 +378,7 @@ func (b *Remote) Configure(obj cty.Value) tfdiags.Diagnostics {
}
// Configure a local backend for when we need to run operations locally.
b.local = backendLocal.NewWithBackend(b)
b.local = backendLocal.NewWithBackend(b, nil)
b.forceLocal = b.forceLocal || !entitlements.Operations
// Enable retries for server errors as the backend is now fully configured.
@ -704,9 +708,8 @@ func (b *Remote) StateMgr(name string) (statemgr.Full, error) {
runID: os.Getenv("TFE_RUN_ID"),
}
return &remote.State{
Client: client,
state := remote.NewState(client, b.encryption)
if client.runID != "" {
// client.runID will be set if we're running a Terraform Cloud
// or Terraform Enterprise remote execution environment, in which
// case we'll disable intermediate snapshots to avoid extra storage
@ -714,8 +717,9 @@ func (b *Remote) StateMgr(name string) (statemgr.Full, error) {
// Other implementations of the remote state protocol should not run
// in contexts where there's a "TFE Run ID" and so are not affected
// by this special case.
DisableIntermediateSnapshots: client.runID != "",
}, nil
state.DisableIntermediateSnapshots()
}
return state, nil
}
func isLocalExecutionMode(execMode string) bool {

View File

@ -18,6 +18,7 @@ import (
tfe "github.com/hashicorp/go-tfe"
"github.com/opentofu/opentofu/internal/command/jsonstate"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -95,7 +96,8 @@ func (r *remoteClient) Put(state []byte) error {
ctx := context.Background()
// Read the raw state into a OpenTofu state.
stateFile, err := statefile.Read(bytes.NewReader(state))
// State Encryption is not supported for the remote backend
stateFile, err := statefile.Read(bytes.NewReader(state), encryption.StateEncryptionDisabled())
if err != nil {
return fmt.Errorf("error reading state: %w", err)
}

View File

@ -11,6 +11,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/cloud"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statefile"
@ -52,7 +53,7 @@ func TestRemoteClient_Put_withRunID(t *testing.T) {
// Create a new empty state.
sf := statefile.New(states.NewState(), "", 0)
var buf bytes.Buffer
statefile.Write(sf, &buf)
statefile.Write(sf, &buf, encryption.StateEncryptionDisabled())
// Store the new state to verify (this will be done
// by the mock that is used) that the run ID is set.

View File

@ -16,6 +16,7 @@ import (
version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-svchost/disco"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
tfversion "github.com/opentofu/opentofu/version"
@ -25,8 +26,8 @@ import (
)
func TestRemote(t *testing.T) {
var _ backend.Enhanced = New(nil)
var _ backend.CLI = New(nil)
var _ backend.Enhanced = New(nil, encryption.StateEncryptionDisabled())
var _ backend.CLI = New(nil, encryption.StateEncryptionDisabled())
}
func TestRemote_backendDefault(t *testing.T) {
@ -153,7 +154,7 @@ func TestRemote_config(t *testing.T) {
for name, tc := range cases {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
// Validate
_, valDiags := b.PrepareConfig(tc.config)
@ -228,7 +229,7 @@ func TestRemote_versionConstraints(t *testing.T) {
for name, tc := range cases {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
// Set the version for this test.
tfversion.Prerelease = tc.prerelease
@ -783,7 +784,7 @@ func TestRemote_VerifyWorkspaceTerraformVersion_ignoreFlagSet(t *testing.T) {
func TestRemote_ServiceDiscoveryAliases(t *testing.T) {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
diag := b.Configure(cty.ObjectVal(map[string]cty.Value{
"hostname": cty.StringVal(mockedBackendHost),

View File

@ -24,6 +24,7 @@ import (
"github.com/opentofu/opentofu/internal/cloud"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states/remote"
@ -123,7 +124,7 @@ func testRemoteClient(t *testing.T) remote.Client {
func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
// Configure the backend so the client is created.
newObj, valDiags := b.PrepareConfig(obj)
@ -181,7 +182,7 @@ func testBackend(t *testing.T, obj cty.Value) (*Remote, func()) {
}
func testLocalBackend(t *testing.T, remote *Remote) backend.Enhanced {
b := backendLocal.NewWithBackend(remote)
b := backendLocal.NewWithBackend(remote, nil)
// Add a test provider to the local backend.
p := backendLocal.TestLocalProvider(t, b, "null", providers.ProviderSchema{

View File

@ -12,6 +12,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/backend/remote"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
@ -211,7 +212,7 @@ func getBackend(cfg cty.Value) (backend.Backend, cty.Value, tfdiags.Diagnostics)
))
return nil, cty.NilVal, diags
}
b := f()
b := f(encryption.StateEncryptionTODO())
config := cfg.GetAttr("config")
if config.IsNull() {

View File

@ -13,6 +13,7 @@ import (
"github.com/apparentlymart/go-dump/dump"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
"github.com/zclconf/go-cty/cty"
@ -320,7 +321,7 @@ func TestState_validation(t *testing.T) {
// the validation step in isolation does not attempt to configure
// the backend.
overrideBackendFactories = map[string]backend.InitFn{
"failsconfigure": func() backend.Backend {
"failsconfigure": func(enc encryption.StateEncryption) backend.Backend {
return backendFailsConfigure{}
},
}

View File

@ -30,6 +30,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/command/jsonformat"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -107,6 +108,8 @@ type Cloud struct {
// input stores the value of the -input flag, since it will be used
// to determine whether or not to ask the user for approval of a run.
input bool
encryption encryption.StateEncryption
}
var _ backend.Backend = (*Cloud)(nil)
@ -114,9 +117,10 @@ var _ backend.Enhanced = (*Cloud)(nil)
var _ backend.Local = (*Cloud)(nil)
// New creates a new initialized cloud backend.
func New(services *disco.Disco) *Cloud {
func New(services *disco.Disco, enc encryption.StateEncryption) *Cloud {
return &Cloud{
services: services,
services: services,
encryption: enc,
}
}
@ -409,7 +413,7 @@ func (b *Cloud) Configure(obj cty.Value) tfdiags.Diagnostics {
}
// Configure a local backend for when we need to run operations locally.
b.local = backendLocal.NewWithBackend(b)
b.local = backendLocal.NewWithBackend(b, nil)
b.forceLocal = b.forceLocal || !entitlements.Operations
// Enable retries for server errors as the backend is now fully configured.
@ -641,7 +645,7 @@ func (b *Cloud) DeleteWorkspace(name string, force bool) error {
}
// Configure the remote workspace name.
State := &State{tfeClient: b.client, organization: b.organization, workspace: workspace, enableIntermediateSnapshots: false}
State := &State{tfeClient: b.client, organization: b.organization, workspace: workspace, enableIntermediateSnapshots: false, encryption: b.encryption}
return State.Delete(force)
}
@ -776,7 +780,7 @@ func (b *Cloud) StateMgr(name string) (statemgr.Full, error) {
}
}
return &State{tfeClient: b.client, organization: b.organization, workspace: workspace, enableIntermediateSnapshots: false}, nil
return &State{tfeClient: b.client, organization: b.organization, workspace: workspace, enableIntermediateSnapshots: false, encryption: b.encryption}, nil
}
// Operation implements backend.Enhanced.

View File

@ -15,6 +15,7 @@ import (
tfe "github.com/hashicorp/go-tfe"
version "github.com/hashicorp/go-version"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/tfdiags"
tfversion "github.com/opentofu/opentofu/version"
"github.com/zclconf/go-cty/cty"
@ -23,8 +24,8 @@ import (
)
func TestCloud(t *testing.T) {
var _ backend.Enhanced = New(nil)
var _ backend.CLI = New(nil)
var _ backend.Enhanced = New(nil, encryption.StateEncryptionDisabled())
var _ backend.CLI = New(nil, encryption.StateEncryptionDisabled())
}
func TestCloud_backendWithName(t *testing.T) {
@ -55,7 +56,7 @@ func TestCloud_backendWithName(t *testing.T) {
func TestCloud_backendWithoutHost(t *testing.T) {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
obj := cty.ObjectVal(map[string]cty.Value{
"hostname": cty.NullVal(cty.String),
@ -175,7 +176,7 @@ func TestCloud_PrepareConfig(t *testing.T) {
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
// Validate
_, valDiags := b.PrepareConfig(tc.config)
@ -326,7 +327,7 @@ func TestCloud_PrepareConfigWithEnvVars(t *testing.T) {
for name, tc := range cases {
t.Run(name, func(t *testing.T) {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
for k, v := range tc.vars {
t.Setenv(k, v)
@ -487,7 +488,7 @@ func TestCloud_configVerifyMinimumTFEVersion(t *testing.T) {
}
s := testServerWithHandlers(handlers)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
confDiags := b.Configure(config)
if confDiags.Err() == nil {
@ -524,7 +525,7 @@ func TestCloud_configVerifyMinimumTFEVersionInAutomation(t *testing.T) {
}
s := testServerWithHandlers(handlers)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
b.runningInAutomation = true
confDiags := b.Configure(config)
@ -1286,7 +1287,7 @@ func TestCloudBackend_DeleteWorkspace_DoesNotExist(t *testing.T) {
func TestCloud_ServiceDiscoveryAliases(t *testing.T) {
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
diag := b.Configure(cty.ObjectVal(map[string]cty.Value{
"hostname": cty.StringVal(tfeHost),

View File

@ -29,6 +29,7 @@ import (
"github.com/opentofu/opentofu/internal/backend/local"
"github.com/opentofu/opentofu/internal/command/jsonstate"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/remote"
"github.com/opentofu/opentofu/internal/states/statefile"
@ -79,6 +80,8 @@ type State struct {
// If the header X-Terraform-Snapshot-Interval is present then
// we will enable snapshots
enableIntermediateSnapshots bool
encryption encryption.StateEncryption
}
var ErrStateVersionUnauthorizedUpgradeState = errors.New(strings.TrimSpace(`
@ -204,7 +207,7 @@ func (s *State) PersistState(schemas *tofu.Schemas) error {
f := statefile.New(s.state, s.lineage, s.serial)
var buf bytes.Buffer
err := statefile.Write(f, &buf)
err := statefile.Write(f, &buf, s.encryption)
if err != nil {
return err
}
@ -217,7 +220,7 @@ func (s *State) PersistState(schemas *tofu.Schemas) error {
}
}
stateFile, err := statefile.Read(bytes.NewReader(buf.Bytes()))
stateFile, err := statefile.Read(bytes.NewReader(buf.Bytes()), s.encryption)
if err != nil {
return fmt.Errorf("failed to read state: %w", err)
}
@ -387,7 +390,7 @@ func (s *State) refreshState() error {
return nil
}
stateFile, err := statefile.Read(bytes.NewReader(payload.Data))
stateFile, err := statefile.Read(bytes.NewReader(payload.Data), s.encryption)
if err != nil {
return err
}

View File

@ -15,6 +15,7 @@ import (
tfe "github.com/hashicorp/go-tfe"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/backend/local"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -42,7 +43,7 @@ func TestState_GetRootOutputValues(t *testing.T) {
state := &State{tfeClient: b.client, organization: b.organization, workspace: &tfe.Workspace{
ID: "ws-abcd",
}}
}, encryption: encryption.StateEncryptionDisabled()}
outputs, err := state.GetRootOutputValues()
if err != nil {
@ -94,7 +95,7 @@ func TestState(t *testing.T) {
var buf bytes.Buffer
s := statemgr.TestFullInitialState()
sf := statefile.New(s, "stub-lineage", 2)
err := statefile.Write(sf, &buf)
err := statefile.Write(sf, &buf, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %s", err)
}

View File

@ -31,6 +31,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/httpclient"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states"
@ -242,7 +243,7 @@ func testBackend(t *testing.T, obj cty.Value, handlers map[string]func(http.Resp
} else {
s = testServer(t)
}
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
// Configure the backend so the client is created.
newObj, valDiags := b.PrepareConfig(obj)
@ -319,7 +320,7 @@ func testUnconfiguredBackend(t *testing.T) (*Cloud, func()) {
skipIfTFENotEnabled(t)
s := testServer(t)
b := New(testDisco(s))
b := New(testDisco(s), encryption.StateEncryptionDisabled())
// Normally, the client is created during configuration, but the configuration uses the
// client to read entitlements.
@ -369,7 +370,7 @@ func testUnconfiguredBackend(t *testing.T) (*Cloud, func()) {
func testLocalBackend(t *testing.T, cloud *Cloud) backend.Enhanced {
skipIfTFENotEnabled(t)
b := backendLocal.NewWithBackend(cloud)
b := backendLocal.NewWithBackend(cloud, nil)
// Add a test provider to the local backend.
p := backendLocal.TestLocalProvider(t, b, "null", providers.ProviderSchema{
@ -426,7 +427,7 @@ func testServerWithSnapshotsEnabled(t *testing.T, enabled bool) *httptest.Server
fakeState := states.NewState()
fakeStateFile := statefile.New(fakeState, "boop", 1)
var buf bytes.Buffer
statefile.Write(fakeStateFile, &buf)
statefile.Write(fakeStateFile, &buf, encryption.StateEncryptionDisabled())
respBody := buf.Bytes()
w.Header().Set("content-type", "application/json")
w.Header().Set("content-length", strconv.FormatInt(int64(len(respBody)), 10))

View File

@ -16,6 +16,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
@ -92,7 +93,7 @@ func TestApply_destroy(t *testing.T) {
}
defer f.Close()
stateFile, err := statefile.Read(f)
stateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %s", err)
}
@ -112,7 +113,7 @@ func TestApply_destroy(t *testing.T) {
t.Fatalf("err: %s", err)
}
backupStateFile, err := statefile.Read(f)
backupStateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -488,7 +489,7 @@ func TestApply_destroyTargetedDependencies(t *testing.T) {
}
defer f.Close()
stateFile, err := statefile.Read(f)
stateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %s", err)
}
@ -507,7 +508,7 @@ func TestApply_destroyTargetedDependencies(t *testing.T) {
t.Fatalf("err: %s", err)
}
backupStateFile, err := statefile.Read(f)
backupStateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -639,7 +640,7 @@ func TestApply_destroyTargeted(t *testing.T) {
}
defer f.Close()
stateFile, err := statefile.Read(f)
stateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %s", err)
}
@ -659,7 +660,7 @@ func TestApply_destroyTargeted(t *testing.T) {
t.Fatalf("err: %s", err)
}
backupStateFile, err := statefile.Read(f)
backupStateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)

View File

@ -24,6 +24,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states"
@ -450,7 +451,7 @@ func TestApply_defaultState(t *testing.T) {
}
// create an existing state file
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem(statePath), states.NewState(), nil); err != nil {
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem(statePath, encryption.StateEncryptionDisabled()), states.NewState(), nil); err != nil {
t.Fatal(err)
}
@ -738,7 +739,7 @@ func TestApply_plan_backup(t *testing.T) {
}
// create a state file that needs to be backed up
fs := statemgr.NewFilesystem(statePath)
fs := statemgr.NewFilesystem(statePath, encryption.StateEncryptionDisabled())
fs.StateSnapshotMeta()
if err := statemgr.WriteAndPersist(fs, states.NewState(), nil); err != nil {
t.Fatal(err)

View File

@ -327,7 +327,7 @@ func writeStateForTesting(state *states.State, w io.Writer) error {
Lineage: "fake-for-testing",
State: state,
}
return statefile.Write(sf, w)
return statefile.Write(sf, w, encryption.StateEncryptionDisabled())
}
// testStateMgrCurrentLineage returns the current lineage for the given state
@ -482,7 +482,7 @@ func testStateRead(t *testing.T, path string) *states.State {
}
defer f.Close()
sf, err := statefile.Read(f)
sf, err := statefile.Read(f, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %s", err)
}
@ -760,7 +760,7 @@ func testBackendState(t *testing.T, s *states.State, c int) (*legacy.State, *htt
// If a state was given, make sure we calculate the proper b64md5
if s != nil {
err := statefile.Write(&statefile.File{State: s}, buf)
err := statefile.Write(&statefile.File{State: s}, buf, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %v", err)
}
@ -774,7 +774,7 @@ func testBackendState(t *testing.T, s *states.State, c int) (*legacy.State, *htt
Type: "http",
Config: configs.SynthBody("<testBackendState>", map[string]cty.Value{}),
}
b := backendInit.Backend("http")()
b := backendInit.Backend("http")(encryption.StateEncryptionDisabled())
configSchema := b.ConfigSchema()
hash := backendConfig.Hash(configSchema)
@ -833,7 +833,7 @@ func testRemoteState(t *testing.T, s *states.State, c int) (*legacy.State, *http
retState.Backend = b
if s != nil {
err := statefile.Write(&statefile.File{State: s}, buf)
err := statefile.Write(&statefile.File{State: s}, buf, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("failed to write initial state: %v", err)
}

View File

@ -478,7 +478,7 @@ func (c *InitCommand) initBackend(ctx context.Context, root *configs.Module, ext
return nil, true, diags
}
b := bf()
b := bf(nil) // This is only used to get the schema, encryption should panic if attempted
backendSchema := b.ConfigSchema()
backendConfig = root.Backend

View File

@ -27,6 +27,7 @@ import (
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/depsfile"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/getproviders"
"github.com/opentofu/opentofu/internal/providercache"
"github.com/opentofu/opentofu/internal/states"
@ -1262,7 +1263,7 @@ func TestInit_inputFalse(t *testing.T) {
false, // not sensitive
)
})
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem("foo"), fooState, nil); err != nil {
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem("foo", encryption.StateEncryptionDisabled()), fooState, nil); err != nil {
t.Fatal(err)
}
barState := states.BuildState(func(s *states.SyncState) {
@ -1272,7 +1273,7 @@ func TestInit_inputFalse(t *testing.T) {
false, // not sensitive
)
})
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem("bar"), barState, nil); err != nil {
if err := statemgr.WriteAndPersist(statemgr.NewFilesystem("bar", encryption.StateEncryptionDisabled()), barState, nil); err != nil {
t.Fatal(err)
}

View File

@ -30,6 +30,7 @@ import (
"github.com/opentofu/opentofu/internal/command/clistate"
"github.com/opentofu/opentofu/internal/command/views"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -181,7 +182,7 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, tfdiags.Diagnostics
}
// Build the local backend
local := backendLocal.NewWithBackend(b)
local := backendLocal.NewWithBackend(b, encryption.StateEncryptionTODO())
if err := local.CLIInit(cliOpts); err != nil {
// Local backend isn't allowed to fail. It would be a bug.
panic(err)
@ -312,7 +313,7 @@ func (m *Meta) BackendForLocalPlan(settings plans.Backend) (backend.Enhanced, tf
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), settings.Type))
return nil, diags
}
b := f()
b := f(encryption.StateEncryptionTODO())
log.Printf("[TRACE] Meta.BackendForLocalPlan: instantiated backend of type %T", b)
schema := b.ConfigSchema()
@ -371,7 +372,7 @@ func (m *Meta) BackendForLocalPlan(settings plans.Backend) (backend.Enhanced, tf
return nil, diags
}
cliOpts.Validation = false // don't validate here in case config contains file(...) calls where the file doesn't exist
local := backendLocal.NewWithBackend(b)
local := backendLocal.NewWithBackend(b, encryption.StateEncryptionTODO())
if err := local.CLIInit(cliOpts); err != nil {
// Local backend should never fail, so this is always a bug.
panic(err)
@ -493,7 +494,7 @@ func (m *Meta) backendConfig(opts *BackendOpts) (*configs.Backend, int, tfdiags.
})
return nil, 0, diags
}
b := bf()
b := bf(encryption.StateEncryptionTODO())
configSchema := b.ConfigSchema()
configBody := c.Config
@ -791,25 +792,25 @@ func (m *Meta) backendFromState(ctx context.Context) (backend.Backend, tfdiags.D
if s == nil {
// no state, so return a local backend
log.Printf("[TRACE] Meta.Backend: backend has not previously been initialized in this working directory")
return backendLocal.New(), diags
return backendLocal.New(encryption.StateEncryptionTODO()), diags
}
if s.Backend == nil {
// s.Backend is nil, so return a local backend
log.Printf("[TRACE] Meta.Backend: working directory was previously initialized but has no backend (is using legacy remote state?)")
return backendLocal.New(), diags
return backendLocal.New(encryption.StateEncryptionTODO()), diags
}
log.Printf("[TRACE] Meta.Backend: working directory was previously initialized for %q backend", s.Backend.Type)
//backend init function
if s.Backend.Type == "" {
return backendLocal.New(), diags
return backendLocal.New(encryption.StateEncryptionTODO()), diags
}
f := backendInit.Backend(s.Backend.Type)
if f == nil {
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
return nil, diags
}
b := f()
b := f(encryption.StateEncryptionTODO())
// The configuration saved in the working directory state file is used
// in this case, since it will contain any additional values that
@ -1266,7 +1267,7 @@ func (m *Meta) savedBackend(sMgr *clistate.LocalState) (backend.Backend, tfdiags
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendSavedUnknown), s.Backend.Type))
return nil, diags
}
b := f()
b := f(encryption.StateEncryptionTODO())
// The configuration saved in the working directory state file is used
// in this case, since it will contain any additional values that
@ -1354,7 +1355,7 @@ func (m *Meta) backendConfigNeedsMigration(c *configs.Backend, s *legacy.Backend
log.Printf("[TRACE] backendConfigNeedsMigration: no backend of type %q, which migration codepath must handle", c.Type)
return true // let the migration codepath deal with the missing backend
}
b := f()
b := f(encryption.StateEncryptionTODO())
schema := b.ConfigSchema()
decSpec := schema.NoneRequired().DecoderSpec()
@ -1391,7 +1392,7 @@ func (m *Meta) backendInitFromConfig(c *configs.Backend) (backend.Backend, cty.V
diags = diags.Append(fmt.Errorf(strings.TrimSpace(errBackendNewUnknown), c.Type))
return nil, cty.NilVal, diags
}
b := f()
b := f(encryption.StateEncryptionTODO())
schema := b.ConfigSchema()
decSpec := schema.NoneRequired().DecoderSpec()

View File

@ -22,6 +22,7 @@ import (
"github.com/opentofu/opentofu/internal/command/arguments"
"github.com/opentofu/opentofu/internal/command/clistate"
"github.com/opentofu/opentofu/internal/command/views"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tofu"
@ -497,7 +498,7 @@ func (m *Meta) backendMigrateNonEmptyConfirm(
// Helper to write the state
saveHelper := func(n, path string, s *states.State) error {
return statemgr.WriteAndPersist(statemgr.NewFilesystem(path), s, nil)
return statemgr.WriteAndPersist(statemgr.NewFilesystem(path, encryption.StateEncryptionDisabled()), s, nil)
}
// Write the states

View File

@ -21,6 +21,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/configs"
"github.com/opentofu/opentofu/internal/copy"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
@ -281,7 +282,7 @@ func TestMetaBackend_configureNew(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -355,7 +356,7 @@ func TestMetaBackend_configureNewWithState(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -404,7 +405,7 @@ func TestMetaBackend_configureNewWithoutCopy(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -521,7 +522,7 @@ func TestMetaBackend_configureNewWithStateExisting(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -592,7 +593,7 @@ func TestMetaBackend_configureNewWithStateExistingNoMigrate(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -711,7 +712,7 @@ func TestMetaBackend_configuredChange(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -773,7 +774,7 @@ func TestMetaBackend_reconfigureChange(t *testing.T) {
}
// verify that the old state is still there
s = statemgr.NewFilesystem("local-state.tfstate")
s = statemgr.NewFilesystem("local-state.tfstate", encryption.StateEncryptionDisabled())
if err := s.RefreshState(); err != nil {
t.Fatal(err)
}
@ -1601,7 +1602,7 @@ func TestMetaBackend_planLocal(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -1643,7 +1644,7 @@ func TestMetaBackend_planLocalStatePath(t *testing.T) {
statePath := "foo.tfstate"
// put an initial state there that needs to be backed up
err = statemgr.WriteAndPersist(statemgr.NewFilesystem(statePath), original, nil)
err = statemgr.WriteAndPersist(statemgr.NewFilesystem(statePath, encryption.StateEncryptionDisabled()), original, nil)
if err != nil {
t.Fatal(err)
}
@ -1702,7 +1703,7 @@ func TestMetaBackend_planLocalStatePath(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -1789,7 +1790,7 @@ func TestMetaBackend_planLocalMatch(t *testing.T) {
if err != nil {
t.Fatalf("err: %s", err)
}
actual, err := statefile.Read(f)
actual, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)

View File

@ -24,6 +24,7 @@ import (
backendinit "github.com/opentofu/opentofu/internal/backend/init"
"github.com/opentofu/opentofu/internal/checks"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states"
@ -479,7 +480,7 @@ func TestPlan_outBackend(t *testing.T) {
t.Errorf("wrong backend workspace %q; want %q", got, want)
}
{
httpBackend := backendinit.Backend("http")()
httpBackend := backendinit.Backend("http")(encryption.StateEncryptionDisabled())
schema := httpBackend.ConfigSchema()
got, err := plan.Backend.Config.Decode(schema.ImpliedType())
if err != nil {

View File

@ -22,6 +22,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/configs/configschema"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/providers"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
@ -75,7 +76,7 @@ func TestRefresh(t *testing.T) {
t.Fatalf("err: %s", err)
}
newStateFile, err := statefile.Read(f)
newStateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -218,7 +219,7 @@ func TestRefresh_cwd(t *testing.T) {
t.Fatalf("err: %s", err)
}
newStateFile, err := statefile.Read(f)
newStateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
f.Close()
if err != nil {
t.Fatalf("err: %s", err)
@ -243,7 +244,7 @@ func TestRefresh_defaultState(t *testing.T) {
// default filename.
statePath := testStateFile(t, originalState)
localState := statemgr.NewFilesystem(statePath)
localState := statemgr.NewFilesystem(statePath, encryption.StateEncryptionDisabled())
if err := localState.RefreshState(); err != nil {
t.Fatal(err)
}
@ -569,7 +570,7 @@ func TestRefresh_backup(t *testing.T) {
// Need to put some state content in the output file so that there's
// something to back up.
err = statefile.Write(statefile.New(state, "baz", 0), outf)
err = statefile.Write(statefile.New(state, "baz", 0), outf, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("error writing initial output state file %s", err)
}

View File

@ -339,7 +339,7 @@ func getStateFromPath(path string) (*statefile.File, error) {
defer file.Close()
var stateFile *statefile.File
stateFile, err = statefile.Read(file)
stateFile, err = statefile.Read(file, encryption.StateEncryptionTODO()) // Should we use encryption -> statefile config here?
if err != nil {
return nil, fmt.Errorf("Error reading %s as a statefile: %w", path, err)
}

View File

@ -11,6 +11,7 @@ import (
"time"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -34,7 +35,7 @@ func (c *StateMeta) State() (statemgr.Full, error) {
// use the specified state
if c.statePath != "" {
realState = statemgr.NewFilesystem(c.statePath)
realState = statemgr.NewFilesystem(c.statePath, encryption.StateEncryptionTODO())
} else {
// Load the backend
b, backendDiags := c.Backend(nil)

View File

@ -10,6 +10,7 @@ import (
"fmt"
"strings"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
)
@ -64,7 +65,7 @@ func (c *StatePullCommand) Run(args []string) int {
if stateFile != nil { // we produce no output if the statefile is nil
var buf bytes.Buffer
err = statefile.Write(stateFile, &buf)
err = statefile.Write(stateFile, &buf, encryption.StateEncryptionTODO())
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to write state: %s", err))
return 1

View File

@ -16,6 +16,7 @@ import (
"github.com/opentofu/opentofu/internal/command/arguments"
"github.com/opentofu/opentofu/internal/command/clistate"
"github.com/opentofu/opentofu/internal/command/views"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -68,7 +69,7 @@ func (c *StatePushCommand) Run(args []string) int {
}
// Read the state
srcStateFile, err := statefile.Read(r)
srcStateFile, err := statefile.Read(r, encryption.StateEncryptionTODO()) // Should we use encryption -> statefile config here?
if c, ok := r.(io.Closer); ok {
// Close the reader if possible right now since we're done with it.
c.Close()

View File

@ -13,6 +13,7 @@ import (
"github.com/mitchellh/cli"
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/backend/remote-state/inmem"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
)
@ -264,7 +265,7 @@ func TestStatePush_forceRemoteState(t *testing.T) {
}
// put a dummy state in place, so we have something to force
b := backend.TestBackendConfig(t, inmem.New(), nil)
b := backend.TestBackendConfig(t, inmem.New(encryption.StateEncryptionDisabled()), nil)
sMgr, err := b.StateMgr("test")
if err != nil {
t.Fatal(err)

View File

@ -17,6 +17,7 @@ import (
"github.com/opentofu/opentofu/internal/command/jsonplan"
"github.com/opentofu/opentofu/internal/command/jsonprovider"
"github.com/opentofu/opentofu/internal/command/views/json"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -29,7 +30,7 @@ type Operation interface {
Stopping()
Cancelled(planMode plans.Mode)
EmergencyDumpState(stateFile *statefile.File) error
EmergencyDumpState(stateFile *statefile.File, enc encryption.StateEncryption) error
PlannedChange(change *plans.ResourceInstanceChangeSrc)
Plan(plan *plans.Plan, schemas *tofu.Schemas)
@ -83,9 +84,9 @@ func (v *OperationHuman) Cancelled(planMode plans.Mode) {
}
}
func (v *OperationHuman) EmergencyDumpState(stateFile *statefile.File) error {
func (v *OperationHuman) EmergencyDumpState(stateFile *statefile.File, enc encryption.StateEncryption) error {
stateBuf := new(bytes.Buffer)
jsonErr := statefile.Write(stateFile, stateBuf)
jsonErr := statefile.Write(stateFile, stateBuf, enc)
if jsonErr != nil {
return jsonErr
}
@ -199,9 +200,9 @@ func (v *OperationJSON) Cancelled(planMode plans.Mode) {
}
}
func (v *OperationJSON) EmergencyDumpState(stateFile *statefile.File) error {
func (v *OperationJSON) EmergencyDumpState(stateFile *statefile.File, enc encryption.StateEncryption) error {
stateBuf := new(bytes.Buffer)
jsonErr := statefile.Write(stateFile, stateBuf)
jsonErr := statefile.Write(stateFile, stateBuf, enc)
if jsonErr != nil {
return jsonErr
}

View File

@ -13,6 +13,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/command/arguments"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/lang/globalref"
"github.com/opentofu/opentofu/internal/plans"
"github.com/opentofu/opentofu/internal/states"
@ -67,7 +68,7 @@ func TestOperation_emergencyDumpState(t *testing.T) {
stateFile := statefile.New(nil, "foo", 1)
err := v.EmergencyDumpState(stateFile)
err := v.EmergencyDumpState(stateFile, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("unexpected error dumping state: %s", err)
}
@ -532,7 +533,7 @@ func TestOperationJSON_emergencyDumpState(t *testing.T) {
stateFile := statefile.New(nil, "foo", 1)
stateBuf := new(bytes.Buffer)
err := statefile.Write(stateFile, stateBuf)
err := statefile.Write(stateFile, stateBuf, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatal(err)
}
@ -542,7 +543,7 @@ func TestOperationJSON_emergencyDumpState(t *testing.T) {
t.Fatal(err)
}
err = v.EmergencyDumpState(stateFile)
err = v.EmergencyDumpState(stateFile, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("unexpected error dumping state: %s", err)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/opentofu/opentofu/internal/backend"
"github.com/opentofu/opentofu/internal/backend/local"
"github.com/opentofu/opentofu/internal/backend/remote-state/inmem"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -253,7 +254,7 @@ func TestWorkspace_createWithState(t *testing.T) {
)
})
err := statemgr.WriteAndPersist(statemgr.NewFilesystem("test.tfstate"), originalState, nil)
err := statemgr.WriteAndPersist(statemgr.NewFilesystem("test.tfstate", encryption.StateEncryptionDisabled()), originalState, nil)
if err != nil {
t.Fatal(err)
}
@ -270,13 +271,13 @@ func TestWorkspace_createWithState(t *testing.T) {
}
newPath := filepath.Join(local.DefaultWorkspaceDir, "test", DefaultStateFilename)
envState := statemgr.NewFilesystem(newPath)
envState := statemgr.NewFilesystem(newPath, encryption.StateEncryptionDisabled())
err = envState.RefreshState()
if err != nil {
t.Fatal(err)
}
b := backend.TestBackendConfig(t, inmem.New(), nil)
b := backend.TestBackendConfig(t, inmem.New(encryption.StateEncryptionDisabled()), nil)
sMgr, err := b.StateMgr(workspace)
if err != nil {
t.Fatal(err)

View File

@ -17,6 +17,7 @@ import (
"github.com/opentofu/opentofu/internal/command/arguments"
"github.com/opentofu/opentofu/internal/command/clistate"
"github.com/opentofu/opentofu/internal/command/views"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/tfdiags"
)
@ -150,7 +151,7 @@ func (c *WorkspaceNewCommand) Run(args []string) int {
return 1
}
stateFile, err := statefile.Read(f)
stateFile, err := statefile.Read(f, encryption.StateEncryptionTODO()) // Should we use encryption -> statefile config here?
if err != nil {
c.Ui.Error(err.Error())
return 1

View File

@ -0,0 +1,16 @@
// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package configs
import "github.com/hashicorp/hcl/v2"
type StateEncryption struct {
Type string
Config hcl.Body
TypeRange hcl.Range
DeclRange hcl.Range
}

View File

@ -193,7 +193,7 @@ func (b *binary) StateFromFile(filename string) (*states.State, error) {
}
defer f.Close()
stateFile, err := statefile.Read(f)
stateFile, err := statefile.Read(f, encryption.StateEncryptionDisabled())
if err != nil {
return nil, fmt.Errorf("Error reading statefile: %w", err)
}
@ -231,7 +231,7 @@ func (b *binary) SetLocalState(state *states.State) error {
Lineage: "fake-for-testing",
State: state,
}
return statefile.Write(sf, f)
return statefile.Write(sf, f, encryption.StateEncryptionDisabled())
}
func GoBuild(pkgPath, tmpPrefix string) string {

View File

@ -90,3 +90,21 @@ func (s *stateEncryption) DecryptState(encryptedState []byte) ([]byte, error) {
return nil
})
}
func StateEncryptionDisabled() StateEncryption {
return &stateDisabled{}
}
type stateDisabled struct{}
func (s *stateDisabled) EncryptState(plainState []byte) ([]byte, error) {
return plainState, nil
}
func (s *stateDisabled) DecryptState(encryptedState []byte) ([]byte, error) {
return encryptedState, nil
}
// TODO REMOVEME once state encryption is fully integrated into the codebase
func StateEncryptionTODO() StateEncryption {
return &stateDisabled{}
}

View File

@ -170,7 +170,7 @@ func (r *Reader) ReadStateFile() (*statefile.File, error) {
if err != nil {
return nil, errUnusable(fmt.Errorf("failed to extract state from plan file: %w", err))
}
return statefile.Read(r)
return statefile.Read(r, encryption.StateEncryptionDisabled())
}
}
return nil, errUnusable(statefile.ErrNoState)
@ -188,7 +188,7 @@ func (r *Reader) ReadPrevStateFile() (*statefile.File, error) {
if err != nil {
return nil, errUnusable(fmt.Errorf("failed to extract previous state from plan file: %w", err))
}
return statefile.Read(r)
return statefile.Read(r, encryption.StateEncryptionDisabled())
}
}
return nil, errUnusable(statefile.ErrNoState)

View File

@ -85,7 +85,7 @@ func Create(filename string, args CreateArgs, enc encryption.PlanEncryption) err
if err != nil {
return fmt.Errorf("failed to create embedded tfstate file: %w", err)
}
err = statefile.Write(args.StateFile, w)
err = statefile.Write(args.StateFile, w, encryption.StateEncryptionDisabled())
if err != nil {
return fmt.Errorf("failed to write state snapshot: %w", err)
}
@ -101,7 +101,7 @@ func Create(filename string, args CreateArgs, enc encryption.PlanEncryption) err
if err != nil {
return fmt.Errorf("failed to create embedded tfstate-prev file: %w", err)
}
err = statefile.Write(args.PreviousRunStateFile, w)
err = statefile.Write(args.PreviousRunStateFile, w, encryption.StateEncryptionDisabled())
if err != nil {
return fmt.Errorf("failed to write previous state snapshot: %w", err)
}

View File

@ -14,6 +14,7 @@ import (
uuid "github.com/hashicorp/go-uuid"
"github.com/opentofu/opentofu/internal/backend/local"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -29,6 +30,8 @@ type State struct {
Client Client
encryption encryption.StateEncryption
// We track two pieces of meta data in addition to the state itself:
//
// lineage - the state's unique ID
@ -47,13 +50,24 @@ type State struct {
// state snapshots created while a OpenTofu Core apply operation is in
// progress. Otherwise (by default) it will accept persistent snapshots
// using the default rules defined in the local backend.
DisableIntermediateSnapshots bool
disableIntermediateSnapshots bool
}
var _ statemgr.Full = (*State)(nil)
var _ statemgr.Migrator = (*State)(nil)
var _ local.IntermediateStateConditionalPersister = (*State)(nil)
func NewState(client Client, enc encryption.StateEncryption) *State {
return &State{
Client: client,
encryption: enc,
}
}
func (s *State) DisableIntermediateSnapshots() {
s.disableIntermediateSnapshots = true
}
// statemgr.Reader impl.
func (s *State) State() *states.State {
s.mu.Lock()
@ -150,7 +164,7 @@ func (s *State) refreshState() error {
return nil
}
stateFile, err := statefile.Read(bytes.NewReader(payload.Data))
stateFile, err := statefile.Read(bytes.NewReader(payload.Data), s.encryption)
if err != nil {
return err
}
@ -207,7 +221,7 @@ func (s *State) PersistState(schemas *tofu.Schemas) error {
f := statefile.New(s.state, s.lineage, s.serial)
var buf bytes.Buffer
err := statefile.Write(f, &buf)
err := statefile.Write(f, &buf, s.encryption)
if err != nil {
return err
}
@ -231,7 +245,7 @@ func (s *State) PersistState(schemas *tofu.Schemas) error {
// ShouldPersistIntermediateState implements local.IntermediateStateConditionalPersister
func (s *State) ShouldPersistIntermediateState(info *local.IntermediateStatePersistInfo) bool {
if s.DisableIntermediateSnapshots {
if s.disableIntermediateSnapshots {
return false
}
return local.DefaultIntermediateStatePersistRule(info)

View File

@ -15,6 +15,7 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
@ -32,9 +33,7 @@ func TestState_impl(t *testing.T) {
}
func TestStateRace(t *testing.T) {
s := &State{
Client: nilClient{},
}
s := NewState(nilClient{}, encryption.StateEncryptionDisabled())
current := states.NewState()
@ -327,9 +326,10 @@ func TestStatePersist(t *testing.T) {
// Initial setup of state just to give us a fixed starting point for our
// test assertions below, or else we'd need to deal with
// random lineage.
mgr := &State{
Client: &mockClient{},
}
mgr := NewState(
&mockClient{},
encryption.StateEncryptionDisabled(),
)
// In normal use (during a OpenTofu operation) we always refresh and read
// before any writes would happen, so we'll mimic that here for realism.
@ -389,8 +389,8 @@ func TestStatePersist(t *testing.T) {
func TestState_GetRootOutputValues(t *testing.T) {
// Initial setup of state with outputs already defined
mgr := &State{
Client: &mockClient{
mgr := NewState(
&mockClient{
current: []byte(`
{
"version": 4,
@ -402,7 +402,8 @@ func TestState_GetRootOutputValues(t *testing.T) {
}
`),
},
}
encryption.StateEncryptionDisabled(),
)
outputs, err := mgr.GetRootOutputValues()
if err != nil {
@ -427,8 +428,8 @@ type migrationTestCase struct {
}
func TestWriteStateForMigration(t *testing.T) {
mgr := &State{
Client: &mockClient{
mgr := NewState(
&mockClient{
current: []byte(`
{
"version": 4,
@ -440,7 +441,8 @@ func TestWriteStateForMigration(t *testing.T) {
}
`),
},
}
encryption.StateEncryptionDisabled(),
)
testCases := []migrationTestCase{
// Refreshing state before we run the test loop causes a GET
@ -583,8 +585,8 @@ func TestWriteStateForMigration(t *testing.T) {
// us to test that -force continues to work for backends without
// this interface, but that this interface works for those that do.
func TestWriteStateForMigrationWithForcePushClient(t *testing.T) {
mgr := &State{
Client: &mockClientForcePusher{
mgr := NewState(
&mockClientForcePusher{
current: []byte(`
{
"version": 4,
@ -596,7 +598,8 @@ func TestWriteStateForMigrationWithForcePushClient(t *testing.T) {
}
`),
},
}
encryption.StateEncryptionDisabled(),
)
testCases := []migrationTestCase{
// Refreshing state before we run the test loop causes a GET

View File

@ -9,6 +9,7 @@ import (
"bytes"
"testing"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/states/statemgr"
)
@ -18,7 +19,7 @@ func TestClient(t *testing.T, c Client) {
var buf bytes.Buffer
s := statemgr.TestFullInitialState()
sf := statefile.New(s, "stub-lineage", 2)
err := statefile.Write(sf, &buf)
err := statefile.Write(sf, &buf, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %s", err)
}

View File

@ -8,6 +8,7 @@ package statefile
import (
"bytes"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
)
@ -30,12 +31,12 @@ func StatesMarshalEqual(a, b *states.State) bool {
// We write here some temporary files that have no header information
// populated, thus ensuring that we're only comparing the state itself
// and not any metadata.
err := Write(&File{State: a}, &aBuf)
err := Write(&File{State: a}, &aBuf, encryption.StateEncryptionDisabled())
if err != nil {
// Should never happen, because we're writing to an in-memory buffer
panic(err)
}
err = Write(&File{State: b}, &bBuf)
err = Write(&File{State: b}, &bBuf, encryption.StateEncryptionDisabled())
if err != nil {
// Should never happen, because we're writing to an in-memory buffer
panic(err)

View File

@ -14,6 +14,7 @@ import (
version "github.com/hashicorp/go-version"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/tfdiags"
tfversion "github.com/opentofu/opentofu/version"
)
@ -49,7 +50,7 @@ func (e *ErrUnusableState) Unwrap() error {
// If the state file is empty, the special error value ErrNoState is returned.
// Otherwise, the returned error might be a wrapper around tfdiags.Diagnostics
// potentially describing multiple errors.
func Read(r io.Reader) (*File, error) {
func Read(r io.Reader, enc encryption.StateEncryption) (*File, error) {
// Some callers provide us a "typed nil" *os.File here, which would
// cause us to panic below if we tried to use it.
if f, ok := r.(*os.File); ok && f == nil {
@ -75,7 +76,10 @@ func Read(r io.Reader) (*File, error) {
return nil, ErrNoState
}
state, err := readState(src)
decrypted, decDiags := enc.DecryptState(src)
diags = diags.Append(decDiags)
state, err := readState(decrypted)
if err != nil {
return nil, err
}

View File

@ -6,6 +6,8 @@ import (
"errors"
"os"
"testing"
"github.com/opentofu/opentofu/internal/encryption"
)
func TestReadErrNoState_emptyFile(t *testing.T) {
@ -15,7 +17,7 @@ func TestReadErrNoState_emptyFile(t *testing.T) {
}
defer emptyFile.Close()
_, err = Read(emptyFile)
_, err = Read(emptyFile, encryption.StateEncryptionDisabled())
if !errors.Is(err, ErrNoState) {
t.Fatalf("expected ErrNoState, got %T", err)
}
@ -27,7 +29,7 @@ func TestReadErrNoState_nilFile(t *testing.T) {
t.Fatal("wrongly succeeded in opening non-existent file")
}
_, err = Read(nilFile)
_, err = Read(nilFile, encryption.StateEncryptionDisabled())
if !errors.Is(err, ErrNoState) {
t.Fatalf("expected ErrNoState, got %T", err)
}

View File

@ -14,6 +14,7 @@ import (
"testing"
"github.com/go-test/deep"
"github.com/opentofu/opentofu/internal/encryption"
)
func TestRoundtrip(t *testing.T) {
@ -53,13 +54,13 @@ func TestRoundtrip(t *testing.T) {
}
defer ir.Close()
f, err := Read(ir)
f, err := Read(ir, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
var buf bytes.Buffer
err = Write(f, &buf)
err = Write(f, &buf, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatal(err)
}

View File

@ -17,6 +17,7 @@ import (
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/checks"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/lang/marks"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/tfdiags"
@ -313,7 +314,7 @@ func prepareStateV4(sV4 *stateV4) (*File, tfdiags.Diagnostics) {
return file, diags
}
func writeStateV4(file *File, w io.Writer) tfdiags.Diagnostics {
func writeStateV4(file *File, w io.Writer, enc encryption.StateEncryption) tfdiags.Diagnostics {
// Here we'll convert back from the "File" representation to our
// stateV4 struct representation and write that.
//
@ -435,7 +436,10 @@ func writeStateV4(file *File, w io.Writer) tfdiags.Diagnostics {
}
src = append(src, '\n')
_, err = w.Write(src)
encrypted, encDiags := enc.EncryptState(src)
diags = diags.Append(encDiags)
_, err = w.Write(encrypted)
if err != nil {
diags = diags.Append(tfdiags.Sourceless(
tfdiags.Error,

View File

@ -8,16 +8,17 @@ package statefile
import (
"io"
"github.com/opentofu/opentofu/internal/encryption"
tfversion "github.com/opentofu/opentofu/version"
)
// Write writes the given state to the given writer in the current state
// serialization format.
func Write(s *File, w io.Writer) error {
func Write(s *File, w io.Writer, enc encryption.StateEncryption) error {
// Always record the current tofu version in the state.
s.TerraformVersion = tfversion.SemVer
diags := writeStateV4(s, w)
diags := writeStateV4(s, w, enc)
return diags.Err()
}
@ -26,6 +27,6 @@ func Write(s *File, w io.Writer) error {
// intended for use in tests that need to override the current tofu
// version.
func WriteForTest(s *File, w io.Writer) error {
diags := writeStateV4(s, w)
diags := writeStateV4(s, w, encryption.StateEncryptionDisabled())
return diags.Err()
}

View File

@ -18,6 +18,7 @@ import (
multierror "github.com/hashicorp/go-multierror"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
"github.com/opentofu/opentofu/internal/tofu"
@ -62,6 +63,8 @@ type Filesystem struct {
readFile *statefile.File
backupFile *statefile.File
writtenBackup bool
encryption encryption.StateEncryption
}
var (
@ -75,20 +78,22 @@ var (
//
// This is equivalent to calling NewFileSystemBetweenPaths with statePath as
// both of the path arguments.
func NewFilesystem(statePath string) *Filesystem {
func NewFilesystem(statePath string, enc encryption.StateEncryption) *Filesystem {
return &Filesystem{
path: statePath,
readPath: statePath,
path: statePath,
readPath: statePath,
encryption: enc,
}
}
// NewFilesystemBetweenPaths creates a filesystem-based state manager that
// reads an initial snapshot from readPath and then writes all new snapshots to
// writePath.
func NewFilesystemBetweenPaths(readPath, writePath string) *Filesystem {
func NewFilesystemBetweenPaths(readPath, writePath string, enc encryption.StateEncryption) *Filesystem {
return &Filesystem{
path: writePath,
readPath: readPath,
path: writePath,
readPath: readPath,
encryption: enc,
}
}
@ -187,7 +192,7 @@ func (s *Filesystem) persistState(schemas *tofu.Schemas) error {
}
defer bfh.Close()
err = statefile.Write(s.backupFile, bfh)
err = statefile.Write(s.backupFile, bfh, s.encryption)
if err != nil {
return fmt.Errorf("failed to write to local state backup file: %w", err)
}
@ -231,7 +236,7 @@ func (s *Filesystem) persistState(schemas *tofu.Schemas) error {
}
log.Printf("[TRACE] statemgr.Filesystem: writing snapshot at %s", s.path)
if err := statefile.Write(s.file, s.stateFileOut); err != nil {
if err := statefile.Write(s.file, s.stateFileOut, s.encryption); err != nil {
return err
}
@ -303,7 +308,7 @@ func (s *Filesystem) refreshState() error {
reader = s.stateFileOut
}
f, err := statefile.Read(reader)
f, err := statefile.Read(reader, s.encryption)
// if there's no state then a nil file is fine
if err != nil {
if err != statefile.ErrNoState {
@ -492,7 +497,7 @@ func (s *Filesystem) createStateFiles() error {
// If the file already existed with content then that'll be the content
// of our backup file if we write a change later.
s.backupFile, err = statefile.Read(s.stateFileOut)
s.backupFile, err = statefile.Read(s.stateFileOut, s.encryption)
if err != nil {
if err != statefile.ErrNoState {
return err

View File

@ -18,6 +18,7 @@ import (
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states"
"github.com/opentofu/opentofu/internal/states/statefile"
tfversion "github.com/opentofu/opentofu/version"
@ -169,7 +170,7 @@ func TestFilesystem_backup(t *testing.T) {
if err != nil {
t.Fatal(err)
}
bf, err := statefile.Read(bfh)
bf, err := statefile.Read(bfh, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatal(err)
}
@ -210,7 +211,7 @@ func TestFilesystem_backupAndReadPath(t *testing.T) {
Serial: 0,
TerraformVersion: version.Must(version.NewVersion("1.2.3")),
State: outState,
}, outFile)
}, outFile, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("failed to write initial outfile state to %s: %s", outFile.Name(), err)
}
@ -232,14 +233,14 @@ func TestFilesystem_backupAndReadPath(t *testing.T) {
Serial: 0,
TerraformVersion: version.Must(version.NewVersion("1.2.3")),
State: inState,
}, inFile)
}, inFile, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("failed to write initial infile state to %s: %s", inFile.Name(), err)
}
backupPath := outFile.Name() + ".backup"
ls := NewFilesystemBetweenPaths(inFile.Name(), outFile.Name())
ls := NewFilesystemBetweenPaths(inFile.Name(), outFile.Name(), encryption.StateEncryptionDisabled())
ls.SetBackupPath(backupPath)
newState := states.BuildState(func(ss *states.SyncState) {
@ -262,7 +263,7 @@ func TestFilesystem_backupAndReadPath(t *testing.T) {
if err != nil {
t.Fatal(err)
}
bf, err := statefile.Read(bfh)
bf, err := statefile.Read(bfh, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatal(err)
}
@ -276,7 +277,7 @@ func TestFilesystem_backupAndReadPath(t *testing.T) {
if err != nil {
t.Fatal(err)
}
of, err := statefile.Read(ofh)
of, err := statefile.Read(ofh, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatal(err)
}
@ -289,7 +290,7 @@ func TestFilesystem_backupAndReadPath(t *testing.T) {
func TestFilesystem_nonExist(t *testing.T) {
defer testOverrideVersion(t, "1.2.3")()
ls := NewFilesystem("ishouldntexist")
ls := NewFilesystem("ishouldntexist", encryption.StateEncryptionDisabled())
if err := ls.RefreshState(); err != nil {
t.Fatalf("err: %s", err)
}
@ -356,13 +357,13 @@ func testFilesystem(t *testing.T) *Filesystem {
Serial: 0,
TerraformVersion: version.Must(version.NewVersion("1.2.3")),
State: TestFullInitialState(),
}, f)
}, f, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("failed to write initial state to %s: %s", f.Name(), err)
}
f.Close()
ls := NewFilesystem(f.Name())
ls := NewFilesystem(f.Name(), encryption.StateEncryptionDisabled())
if err := ls.RefreshState(); err != nil {
t.Fatalf("initial refresh failed: %s", err)
}
@ -383,13 +384,13 @@ func TestFilesystem_refreshWhileLocked(t *testing.T) {
Serial: 0,
TerraformVersion: version.Must(version.NewVersion("1.2.3")),
State: TestFullInitialState(),
}, f)
}, f, encryption.StateEncryptionDisabled())
if err != nil {
t.Fatalf("err: %s", err)
}
f.Close()
s := NewFilesystem(f.Name())
s := NewFilesystem(f.Name(), encryption.StateEncryptionDisabled())
defer os.Remove(s.path)
// lock first

View File

@ -5,6 +5,7 @@ import (
"log"
"os"
"github.com/opentofu/opentofu/internal/encryption"
"github.com/opentofu/opentofu/internal/states/statemgr"
)
@ -15,7 +16,7 @@ func main() {
log.Fatal(os.Args[0], "statefile")
}
s := statemgr.NewFilesystem(os.Args[1])
s := statemgr.NewFilesystem(os.Args[1], encryption.StateEncryptionDisabled())
info := statemgr.NewLockInfo()
info.Operation = "test"