mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Integrate encryption into plan serialization (#1292)
Signed-off-by: Christian Mesh <christianmesh1@gmail.com>
This commit is contained in:
parent
997e5fa46e
commit
ac3ed86617
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/command/views"
|
"github.com/opentofu/opentofu/internal/command/views"
|
||||||
"github.com/opentofu/opentofu/internal/configs/configload"
|
"github.com/opentofu/opentofu/internal/configs/configload"
|
||||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/initwd"
|
"github.com/opentofu/opentofu/internal/initwd"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/plans/planfile"
|
"github.com/opentofu/opentofu/internal/plans/planfile"
|
||||||
@ -97,7 +98,7 @@ func TestLocalRun_cloudPlan(t *testing.T) {
|
|||||||
|
|
||||||
planPath := "./testdata/plan-bookmark/bookmark.json"
|
planPath := "./testdata/plan-bookmark/bookmark.json"
|
||||||
|
|
||||||
planFile, err := planfile.OpenWrapped(planPath)
|
planFile, err := planfile.OpenWrapped(planPath, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error reading planfile: %s", err)
|
t.Fatalf("unexpected error reading planfile: %s", err)
|
||||||
}
|
}
|
||||||
@ -180,10 +181,10 @@ func TestLocalRun_stalePlan(t *testing.T) {
|
|||||||
StateFile: stateFile,
|
StateFile: stateFile,
|
||||||
Plan: plan,
|
Plan: plan,
|
||||||
}
|
}
|
||||||
if err := planfile.Create(planPath, planfileArgs); err != nil {
|
if err := planfile.Create(planPath, planfileArgs, encryption.PlanEncryptionDisabled()); err != nil {
|
||||||
t.Fatalf("unexpected error writing planfile: %s", err)
|
t.Fatalf("unexpected error writing planfile: %s", err)
|
||||||
}
|
}
|
||||||
planFile, err := planfile.OpenWrapped(planPath)
|
planFile, err := planfile.OpenWrapped(planPath, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error reading planfile: %s", err)
|
t.Fatalf("unexpected error reading planfile: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
|
|
||||||
"github.com/opentofu/opentofu/internal/backend"
|
"github.com/opentofu/opentofu/internal/backend"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/genconfig"
|
"github.com/opentofu/opentofu/internal/genconfig"
|
||||||
"github.com/opentofu/opentofu/internal/logging"
|
"github.com/opentofu/opentofu/internal/logging"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
@ -172,7 +173,7 @@ func (b *Local) opPlan(
|
|||||||
StateFile: plannedStateFile,
|
StateFile: plannedStateFile,
|
||||||
Plan: plan,
|
Plan: plan,
|
||||||
DependencyLocks: op.DependencyLocks,
|
DependencyLocks: op.DependencyLocks,
|
||||||
})
|
}, encryption.PlanEncryptionTODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
diags = diags.Append(tfdiags.Sourceless(
|
diags = diags.Append(tfdiags.Sourceless(
|
||||||
tfdiags.Error,
|
tfdiags.Error,
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/command/views"
|
"github.com/opentofu/opentofu/internal/command/views"
|
||||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||||
"github.com/opentofu/opentofu/internal/depsfile"
|
"github.com/opentofu/opentofu/internal/depsfile"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/initwd"
|
"github.com/opentofu/opentofu/internal/initwd"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/plans/planfile"
|
"github.com/opentofu/opentofu/internal/plans/planfile"
|
||||||
@ -841,11 +842,10 @@ func testPlanState_tainted() *states.State {
|
|||||||
func testReadPlan(t *testing.T, path string) *plans.Plan {
|
func testReadPlan(t *testing.T, path string) *plans.Plan {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
p, err := planfile.Open(path)
|
p, err := planfile.Open(path, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err: %s", err)
|
t.Fatalf("err: %s", err)
|
||||||
}
|
}
|
||||||
defer p.Close()
|
|
||||||
|
|
||||||
plan, err := p.ReadPlan()
|
plan, err := p.ReadPlan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -40,6 +40,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||||
"github.com/opentofu/opentofu/internal/copy"
|
"github.com/opentofu/opentofu/internal/copy"
|
||||||
"github.com/opentofu/opentofu/internal/depsfile"
|
"github.com/opentofu/opentofu/internal/depsfile"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/getproviders"
|
"github.com/opentofu/opentofu/internal/getproviders"
|
||||||
"github.com/opentofu/opentofu/internal/initwd"
|
"github.com/opentofu/opentofu/internal/initwd"
|
||||||
legacy "github.com/opentofu/opentofu/internal/legacy/tofu"
|
legacy "github.com/opentofu/opentofu/internal/legacy/tofu"
|
||||||
@ -228,7 +229,7 @@ func testPlanFileMatchState(t *testing.T, configSnap *configload.Snapshot, state
|
|||||||
StateFile: stateFile,
|
StateFile: stateFile,
|
||||||
Plan: plan,
|
Plan: plan,
|
||||||
DependencyLocks: depsfile.NewLocks(),
|
DependencyLocks: depsfile.NewLocks(),
|
||||||
})
|
}, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create temporary plan file: %s", err)
|
t.Fatalf("failed to create temporary plan file: %s", err)
|
||||||
}
|
}
|
||||||
@ -276,11 +277,10 @@ func testFileEquals(t *testing.T, got, want string) {
|
|||||||
func testReadPlan(t *testing.T, path string) *plans.Plan {
|
func testReadPlan(t *testing.T, path string) *plans.Plan {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
f, err := planfile.Open(path)
|
f, err := planfile.Open(path, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error opening plan file %q: %s", path, err)
|
t.Fatalf("error opening plan file %q: %s", path, err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
p, err := f.ReadPlan()
|
p, err := f.ReadPlan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/plans/planfile"
|
"github.com/opentofu/opentofu/internal/plans/planfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,5 +49,5 @@ func (m *Meta) PlanFile(path string) (*planfile.WrappedPlanFile, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return planfile.OpenWrapped(path)
|
return planfile.OpenWrapped(path, encryption.PlanEncryptionTODO())
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/command/arguments"
|
"github.com/opentofu/opentofu/internal/command/arguments"
|
||||||
"github.com/opentofu/opentofu/internal/command/views"
|
"github.com/opentofu/opentofu/internal/command/views"
|
||||||
"github.com/opentofu/opentofu/internal/configs"
|
"github.com/opentofu/opentofu/internal/configs"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/plans/planfile"
|
"github.com/opentofu/opentofu/internal/plans/planfile"
|
||||||
"github.com/opentofu/opentofu/internal/states/statefile"
|
"github.com/opentofu/opentofu/internal/states/statefile"
|
||||||
@ -272,7 +273,7 @@ func (c *ShowCommand) getPlanFromPath(path string) (*plans.Plan, *cloudplan.Remo
|
|||||||
var stateFile *statefile.File
|
var stateFile *statefile.File
|
||||||
var config *configs.Config
|
var config *configs.Config
|
||||||
|
|
||||||
pf, err := planfile.OpenWrapped(path)
|
pf, err := planfile.OpenWrapped(path, encryption.PlanEncryptionTODO())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, nil, err
|
return nil, nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/plans/planfile"
|
"github.com/opentofu/opentofu/internal/plans/planfile"
|
||||||
"github.com/opentofu/opentofu/internal/states"
|
"github.com/opentofu/opentofu/internal/states"
|
||||||
@ -202,11 +203,10 @@ func (b *binary) StateFromFile(filename string) (*states.State, error) {
|
|||||||
// Plan is a helper for easily reading a plan file from the working directory.
|
// Plan is a helper for easily reading a plan file from the working directory.
|
||||||
func (b *binary) Plan(path string) (*plans.Plan, error) {
|
func (b *binary) Plan(path string) (*plans.Plan, error) {
|
||||||
path = b.Path(path)
|
path = b.Path(path)
|
||||||
pr, err := planfile.Open(path)
|
pr, err := planfile.Open(path, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer pr.Close()
|
|
||||||
plan, err := pr.ReadPlan()
|
plan, err := pr.ReadPlan()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -67,3 +67,21 @@ func (p planEncryption) DecryptPlan(data []byte) ([]byte, error) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PlanEncryptionDisabled() PlanEncryption {
|
||||||
|
return &planDisabled{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type planDisabled struct{}
|
||||||
|
|
||||||
|
func (s *planDisabled) EncryptPlan(plainPlan []byte) ([]byte, error) {
|
||||||
|
return plainPlan, nil
|
||||||
|
}
|
||||||
|
func (s *planDisabled) DecryptPlan(encryptedPlan []byte) ([]byte, error) {
|
||||||
|
return encryptedPlan, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO REMOVEME once plan encryption is fully integrated into the codebase
|
||||||
|
func PlanEncryptionTODO() PlanEncryption {
|
||||||
|
return &planDisabled{}
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/addrs"
|
"github.com/opentofu/opentofu/internal/addrs"
|
||||||
"github.com/opentofu/opentofu/internal/configs/configload"
|
"github.com/opentofu/opentofu/internal/configs/configload"
|
||||||
"github.com/opentofu/opentofu/internal/depsfile"
|
"github.com/opentofu/opentofu/internal/depsfile"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/getproviders"
|
"github.com/opentofu/opentofu/internal/getproviders"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/states"
|
"github.com/opentofu/opentofu/internal/states"
|
||||||
@ -98,12 +99,12 @@ func TestRoundtrip(t *testing.T) {
|
|||||||
StateFile: stateFileIn,
|
StateFile: stateFileIn,
|
||||||
Plan: planIn,
|
Plan: planIn,
|
||||||
DependencyLocks: locksIn,
|
DependencyLocks: locksIn,
|
||||||
})
|
}, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create plan file: %s", err)
|
t.Fatalf("failed to create plan file: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wpf, err := OpenWrapped(planFn)
|
wpf, err := OpenWrapped(planFn, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to open plan file for reading: %s", err)
|
t.Fatalf("failed to open plan file for reading: %s", err)
|
||||||
}
|
}
|
||||||
@ -181,14 +182,14 @@ func TestRoundtrip(t *testing.T) {
|
|||||||
func TestWrappedError(t *testing.T) {
|
func TestWrappedError(t *testing.T) {
|
||||||
// Open something that isn't a cloud or local planfile: should error
|
// Open something that isn't a cloud or local planfile: should error
|
||||||
wrongFile := "not a valid zip file"
|
wrongFile := "not a valid zip file"
|
||||||
_, err := OpenWrapped(filepath.Join("testdata", "test-config", "root.tf"))
|
_, err := OpenWrapped(filepath.Join("testdata", "test-config", "root.tf"), encryption.PlanEncryptionDisabled())
|
||||||
if !strings.Contains(err.Error(), wrongFile) {
|
if !strings.Contains(err.Error(), wrongFile) {
|
||||||
t.Fatalf("expected %q, got %q", wrongFile, err)
|
t.Fatalf("expected %q, got %q", wrongFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open something that doesn't exist: should error
|
// Open something that doesn't exist: should error
|
||||||
missingFile := "no such file or directory"
|
missingFile := "no such file or directory"
|
||||||
_, err = OpenWrapped(filepath.Join("testdata", "absent.tfplan"))
|
_, err = OpenWrapped(filepath.Join("testdata", "absent.tfplan"), encryption.PlanEncryptionDisabled())
|
||||||
if !strings.Contains(err.Error(), missingFile) {
|
if !strings.Contains(err.Error(), missingFile) {
|
||||||
t.Fatalf("expected %q, got %q", missingFile, err)
|
t.Fatalf("expected %q, got %q", missingFile, err)
|
||||||
}
|
}
|
||||||
@ -196,7 +197,7 @@ func TestWrappedError(t *testing.T) {
|
|||||||
|
|
||||||
func TestWrappedCloud(t *testing.T) {
|
func TestWrappedCloud(t *testing.T) {
|
||||||
// Loading valid cloud plan results in a wrapped cloud plan
|
// Loading valid cloud plan results in a wrapped cloud plan
|
||||||
wpf, err := OpenWrapped(filepath.Join("testdata", "cloudplan.json"))
|
wpf, err := OpenWrapped(filepath.Join("testdata", "cloudplan.json"), encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to open valid cloud plan: %s", err)
|
t.Fatalf("failed to open valid cloud plan: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/configs"
|
"github.com/opentofu/opentofu/internal/configs"
|
||||||
"github.com/opentofu/opentofu/internal/configs/configload"
|
"github.com/opentofu/opentofu/internal/configs/configload"
|
||||||
"github.com/opentofu/opentofu/internal/depsfile"
|
"github.com/opentofu/opentofu/internal/depsfile"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/states/statefile"
|
"github.com/opentofu/opentofu/internal/states/statefile"
|
||||||
"github.com/opentofu/opentofu/internal/tfdiags"
|
"github.com/opentofu/opentofu/internal/tfdiags"
|
||||||
@ -50,15 +51,25 @@ func (e *ErrUnusableLocalPlan) Unwrap() error {
|
|||||||
// be used to access the individual portions of the file for further
|
// be used to access the individual portions of the file for further
|
||||||
// processing.
|
// processing.
|
||||||
type Reader struct {
|
type Reader struct {
|
||||||
zip *zip.ReadCloser
|
zip *zip.Reader
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open creates a Reader for the file at the given filename, or returns an error
|
// Open creates a Reader for the file at the given filename, or returns an error
|
||||||
// if the file doesn't seem to be a planfile. NOTE: Most commands that accept a
|
// if the file doesn't seem to be a planfile. NOTE: Most commands that accept a
|
||||||
// plan file should use OpenWrapped instead, so they can support both local and
|
// plan file should use OpenWrapped instead, so they can support both local and
|
||||||
// cloud plan files.
|
// cloud plan files.
|
||||||
func Open(filename string) (*Reader, error) {
|
func Open(filename string, enc encryption.PlanEncryption) (*Reader, error) {
|
||||||
r, err := zip.OpenReader(filename)
|
raw, err := os.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
decrypted, diags := enc.DecryptPlan(raw)
|
||||||
|
if diags != nil {
|
||||||
|
return nil, diags
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := zip.NewReader(bytes.NewReader(decrypted), int64(len(decrypted)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// To give a better error message, we'll sniff to see if this looks
|
// To give a better error message, we'll sniff to see if this looks
|
||||||
// like our old plan format from versions prior to 0.12.
|
// like our old plan format from versions prior to 0.12.
|
||||||
@ -115,7 +126,6 @@ func (r *Reader) ReadPlan() (*plans.Plan, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errUnusable(fmt.Errorf("failed to retrieve plan from plan file: %w", err))
|
return nil, errUnusable(fmt.Errorf("failed to retrieve plan from plan file: %w", err))
|
||||||
}
|
}
|
||||||
defer pr.Close()
|
|
||||||
|
|
||||||
// There's a slight mismatch in how plans.Plan is modeled vs. how
|
// There's a slight mismatch in how plans.Plan is modeled vs. how
|
||||||
// the underlying plan file format works, because the "tfplan" embedded
|
// the underlying plan file format works, because the "tfplan" embedded
|
||||||
@ -190,7 +200,7 @@ func (r *Reader) ReadPrevStateFile() (*statefile.File, error) {
|
|||||||
// This is a lower-level alternative to ReadConfig that just extracts the
|
// This is a lower-level alternative to ReadConfig that just extracts the
|
||||||
// source files, without attempting to parse them.
|
// source files, without attempting to parse them.
|
||||||
func (r *Reader) ReadConfigSnapshot() (*configload.Snapshot, error) {
|
func (r *Reader) ReadConfigSnapshot() (*configload.Snapshot, error) {
|
||||||
return readConfigSnapshot(&r.zip.Reader)
|
return readConfigSnapshot(r.zip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadConfig reads the configuration embedded in the plan file.
|
// ReadConfig reads the configuration embedded in the plan file.
|
||||||
@ -262,8 +272,3 @@ func (r *Reader) ReadDependencyLocks() (*depsfile.Locks, tfdiags.Diagnostics) {
|
|||||||
))
|
))
|
||||||
return nil, diags
|
return nil, diags
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the file, after which no other operations may be performed.
|
|
||||||
func (r *Reader) Close() error {
|
|
||||||
return r.zip.Close()
|
|
||||||
}
|
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/opentofu/opentofu/internal/cloud/cloudplan"
|
"github.com/opentofu/opentofu/internal/cloud/cloudplan"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WrappedPlanFile is a sum type that represents a saved plan, loaded from a
|
// WrappedPlanFile is a sum type that represents a saved plan, loaded from a
|
||||||
@ -76,9 +77,9 @@ func NewWrappedCloud(c *cloudplan.SavedPlanBookmark) *WrappedPlanFile {
|
|||||||
// returns an error if the file doesn't seem to be a plan file of either kind.
|
// returns an error if the file doesn't seem to be a plan file of either kind.
|
||||||
// Most consumers should use this and switch behaviors based on the kind of plan
|
// Most consumers should use this and switch behaviors based on the kind of plan
|
||||||
// they expected, rather than directly using Open.
|
// they expected, rather than directly using Open.
|
||||||
func OpenWrapped(filename string) (*WrappedPlanFile, error) {
|
func OpenWrapped(filename string, enc encryption.PlanEncryption) (*WrappedPlanFile, error) {
|
||||||
// First, try to load it as a local planfile.
|
// First, try to load it as a local planfile.
|
||||||
local, localErr := Open(filename)
|
local, localErr := Open(filename, enc)
|
||||||
if localErr == nil {
|
if localErr == nil {
|
||||||
return &WrappedPlanFile{local: local}, nil
|
return &WrappedPlanFile{local: local}, nil
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,14 @@ package planfile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/zip"
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/opentofu/opentofu/internal/configs/configload"
|
"github.com/opentofu/opentofu/internal/configs/configload"
|
||||||
"github.com/opentofu/opentofu/internal/depsfile"
|
"github.com/opentofu/opentofu/internal/depsfile"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/states/statefile"
|
"github.com/opentofu/opentofu/internal/states/statefile"
|
||||||
)
|
)
|
||||||
@ -53,15 +55,9 @@ type CreateArgs struct {
|
|||||||
// state file in addition to the plan itself, so that OpenTofu can detect
|
// state file in addition to the plan itself, so that OpenTofu can detect
|
||||||
// if the world has changed since the plan was created and thus refuse to
|
// if the world has changed since the plan was created and thus refuse to
|
||||||
// apply it.
|
// apply it.
|
||||||
func Create(filename string, args CreateArgs) error {
|
func Create(filename string, args CreateArgs, enc encryption.PlanEncryption) error {
|
||||||
f, err := os.Create(filename)
|
buff := bytes.NewBuffer(make([]byte, 0))
|
||||||
if err != nil {
|
zw := zip.NewWriter(buff)
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
zw := zip.NewWriter(f)
|
|
||||||
defer zw.Close()
|
|
||||||
|
|
||||||
// tfplan file
|
// tfplan file
|
||||||
{
|
{
|
||||||
@ -140,5 +136,12 @@ func Create(filename string, args CreateArgs) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
// Finish zip file
|
||||||
|
zw.Close()
|
||||||
|
// Encrypt payload
|
||||||
|
encrypted, err := enc.EncryptPlan(buff.Bytes())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(filename, encrypted, 0644)
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/opentofu/opentofu/internal/configs/configload"
|
"github.com/opentofu/opentofu/internal/configs/configload"
|
||||||
"github.com/opentofu/opentofu/internal/configs/configschema"
|
"github.com/opentofu/opentofu/internal/configs/configschema"
|
||||||
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
|
"github.com/opentofu/opentofu/internal/configs/hcl2shim"
|
||||||
|
"github.com/opentofu/opentofu/internal/encryption"
|
||||||
"github.com/opentofu/opentofu/internal/plans"
|
"github.com/opentofu/opentofu/internal/plans"
|
||||||
"github.com/opentofu/opentofu/internal/plans/planfile"
|
"github.com/opentofu/opentofu/internal/plans/planfile"
|
||||||
"github.com/opentofu/opentofu/internal/providers"
|
"github.com/opentofu/opentofu/internal/providers"
|
||||||
@ -716,12 +717,12 @@ func contextOptsForPlanViaFile(t *testing.T, configSnap *configload.Snapshot, pl
|
|||||||
PreviousRunStateFile: prevStateFile,
|
PreviousRunStateFile: prevStateFile,
|
||||||
StateFile: stateFile,
|
StateFile: stateFile,
|
||||||
Plan: plan,
|
Plan: plan,
|
||||||
})
|
}, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, err := planfile.Open(filename)
|
pr, err := planfile.Open(filename, encryption.PlanEncryptionDisabled())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user