4
4
package rpcapi
5
5
6
6
import (
7
+ "bytes"
7
8
"context"
8
9
"fmt"
9
10
"io"
11
+ "os"
12
+ "path/filepath"
10
13
"time"
11
14
12
15
"github.com/hashicorp/go-slug/sourceaddrs"
13
16
"github.com/hashicorp/go-slug/sourcebundle"
17
+ "github.com/hashicorp/terraform-svchost/disco"
14
18
"go.opentelemetry.io/otel/attribute"
15
19
otelCodes "go.opentelemetry.io/otel/codes"
16
20
"go.opentelemetry.io/otel/trace"
17
21
"google.golang.org/grpc/codes"
18
22
"google.golang.org/grpc/status"
19
23
20
24
"github.com/hashicorp/terraform/internal/addrs"
25
+ "github.com/hashicorp/terraform/internal/backend"
26
+ "github.com/hashicorp/terraform/internal/backend/local"
27
+ "github.com/hashicorp/terraform/internal/command/workdir"
21
28
"github.com/hashicorp/terraform/internal/depsfile"
22
29
"github.com/hashicorp/terraform/internal/plans"
23
30
"github.com/hashicorp/terraform/internal/providercache"
24
31
"github.com/hashicorp/terraform/internal/providers"
32
+ "github.com/hashicorp/terraform/internal/rpcapi/terraform1"
25
33
"github.com/hashicorp/terraform/internal/rpcapi/terraform1/stacks"
26
34
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
27
35
"github.com/hashicorp/terraform/internal/stacks/stackconfig"
36
+ "github.com/hashicorp/terraform/internal/stacks/stackmigrate"
28
37
"github.com/hashicorp/terraform/internal/stacks/stackplan"
29
38
"github.com/hashicorp/terraform/internal/stacks/stackruntime"
30
39
"github.com/hashicorp/terraform/internal/stacks/stackruntime/hooks"
31
40
"github.com/hashicorp/terraform/internal/stacks/stackstate"
41
+ "github.com/hashicorp/terraform/internal/states"
42
+ "github.com/hashicorp/terraform/internal/states/statefile"
32
43
"github.com/hashicorp/terraform/internal/tfdiags"
33
44
)
34
45
35
46
type stacksServer struct {
36
47
stacks.UnimplementedStacksServer
37
48
38
49
stopper * stopper
50
+ services * disco.Disco
39
51
handles * handleTable
40
52
experimentsAllowed bool
41
53
@@ -55,9 +67,10 @@ type stacksServer struct {
55
67
56
68
var _ stacks.StacksServer = (* stacksServer )(nil )
57
69
58
- func newStacksServer (stopper * stopper , handles * handleTable , opts * serviceOpts ) * stacksServer {
70
+ func newStacksServer (stopper * stopper , handles * handleTable , services * disco. Disco , opts * serviceOpts ) * stacksServer {
59
71
return & stacksServer {
60
72
stopper : stopper ,
73
+ services : services ,
61
74
handles : handles ,
62
75
experimentsAllowed : opts .experimentsAllowed ,
63
76
}
@@ -79,7 +92,7 @@ func (s *stacksServer) OpenStackConfiguration(ctx context.Context, req *stacks.O
79
92
if diags .HasErrors () {
80
93
// For errors in the configuration itself we treat that as a successful
81
94
// result from OpenStackConfiguration but with diagnostics in the
82
- // response and no source handle.
95
+ // response and no source handle. f
83
96
return & stacks.OpenStackConfiguration_Response {
84
97
Diagnostics : diagnosticsToProto (diags ),
85
98
}, nil
@@ -776,6 +789,142 @@ func (s *stacksServer) InspectExpressionResult(ctx context.Context, req *stacks.
776
789
return insp .InspectExpressionResult (ctx , req )
777
790
}
778
791
792
+ func (s * stacksServer ) OpenTerraformState (ctx context.Context , request * stacks.OpenTerraformState_Request ) (* stacks.OpenTerraformState_Response , error ) {
793
+ switch data := request .State .(type ) {
794
+ case * stacks.OpenTerraformState_Request_ConfigPath :
795
+ // load the state specified by this configuration
796
+
797
+ workingDirectory := workdir .NewDir (data .ConfigPath )
798
+ if data := os .Getenv ("TF_DATA_DIR" ); len (data ) > 0 {
799
+ workingDirectory .OverrideDataDir (data )
800
+ }
801
+
802
+ // Load the currently active workspace from the environment, defaulting
803
+ // to the default workspace if not set.
804
+
805
+ workspace := backend .DefaultStateName
806
+ if ws := os .Getenv ("TF_WORKSPACE" ); len (ws ) > 0 {
807
+ workspace = ws
808
+ }
809
+
810
+ workspaceData , err := os .ReadFile (filepath .Join (workingDirectory .DataDir (), local .DefaultWorkspaceFile ))
811
+ if err != nil && ! os .IsNotExist (err ) {
812
+ return nil , status .Errorf (codes .InvalidArgument , "failed to read workspace file: %s" , err )
813
+ }
814
+ if len (workspaceData ) > 0 {
815
+ workspace = string (workspaceData )
816
+ }
817
+
818
+ // Load the state from the backend specified by the .terraform.tfstate
819
+ // file. This function should return an empty state even if the diags
820
+ // has errors. This makes it easier for the caller, as they should
821
+ // close the state handle regardless of the diags.
822
+ state , diags := stackmigrate .Load (workingDirectory .RootModuleDir (), filepath .Join (workingDirectory .DataDir (), ".terraform.tfstate" ), workspace )
823
+
824
+ hnd := s .handles .NewTerraformState (state )
825
+ return & stacks.OpenTerraformState_Response {
826
+ StateHandle : hnd .ForProtobuf (),
827
+ Diagnostics : diagnosticsToProto (diags ),
828
+ }, nil
829
+
830
+ case * stacks.OpenTerraformState_Request_Raw :
831
+ // load the state from the raw data
832
+
833
+ file , err := statefile .Read (bytes .NewReader (data .Raw ))
834
+ if err != nil {
835
+ return nil , status .Errorf (codes .InvalidArgument , "invalid raw state data: %s" , err )
836
+ }
837
+
838
+ hnd := s .handles .NewTerraformState (file .State )
839
+ return & stacks.OpenTerraformState_Response {
840
+ StateHandle : hnd .ForProtobuf (),
841
+ }, nil
842
+
843
+ default :
844
+ return nil , status .Error (codes .InvalidArgument , "invalid state source" )
845
+ }
846
+ }
847
+
848
+ func (s * stacksServer ) CloseTerraformState (ctx context.Context , request * stacks.CloseTerraformState_Request ) (* stacks.CloseTerraformState_Response , error ) {
849
+ hnd := handle [* states.State ](request .StateHandle )
850
+ err := s .handles .CloseTerraformState (hnd )
851
+ if err != nil {
852
+ return nil , status .Error (codes .InvalidArgument , err .Error ())
853
+ }
854
+ return new (stacks.CloseTerraformState_Response ), nil
855
+ }
856
+
857
+ func (s * stacksServer ) MigrateTerraformState (request * stacks.MigrateTerraformState_Request , server stacks.Stacks_MigrateTerraformStateServer ) error {
858
+
859
+ previousStateHandle := handle [* states.State ](request .StateHandle )
860
+ previousState := s .handles .TerraformState (previousStateHandle )
861
+ if previousState == nil {
862
+ return status .Error (codes .InvalidArgument , "the given state handle is invalid" )
863
+ }
864
+
865
+ configHandle := handle [* stackconfig.Config ](request .ConfigHandle )
866
+ config := s .handles .StackConfig (configHandle )
867
+ if config == nil {
868
+ return status .Error (codes .InvalidArgument , "the given config handle is invalid" )
869
+ }
870
+
871
+ dependencyLocksHandle := handle [* depsfile.Locks ](request .DependencyLocksHandle )
872
+ dependencyLocks := s .handles .DependencyLocks (dependencyLocksHandle )
873
+ if dependencyLocks == nil {
874
+ return status .Error (codes .InvalidArgument , "the given dependency locks handle is invalid" )
875
+ }
876
+
877
+ providerCacheHandle := handle [* providercache.Dir ](request .ProviderCacheHandle )
878
+ providerCache := s .handles .ProviderPluginCache (providerCacheHandle )
879
+ if providerCache == nil {
880
+ return status .Error (codes .InvalidArgument , "the given provider cache handle is invalid" )
881
+ }
882
+
883
+ providerFactories , err := providerFactoriesForLocks (dependencyLocks , providerCache )
884
+ if err != nil {
885
+ return status .Errorf (codes .InvalidArgument , "provider dependencies are inconsistent: %s" , err )
886
+ }
887
+
888
+ migrate := & stackmigrate.Migration {
889
+ Providers : providerFactories ,
890
+ PreviousState : previousState ,
891
+ Config : config ,
892
+ }
893
+
894
+ emit := func (change stackstate.AppliedChange ) {
895
+ proto , err := change .AppliedChangeProto ()
896
+ if err != nil {
897
+ server .Send (& stacks.MigrateTerraformState_Event {
898
+ Result : & stacks.MigrateTerraformState_Event_Diagnostic {
899
+ Diagnostic : & terraform1.Diagnostic {
900
+ Severity : terraform1 .Diagnostic_ERROR ,
901
+ Summary : "Failed to serialize change" ,
902
+ Detail : fmt .Sprintf ("Failed to serialize state change for recording in the migration plan: %s" , err ),
903
+ },
904
+ },
905
+ })
906
+ return
907
+ }
908
+
909
+ server .Send (& stacks.MigrateTerraformState_Event {
910
+ Result : & stacks.MigrateTerraformState_Event_AppliedChange {
911
+ AppliedChange : proto ,
912
+ },
913
+ })
914
+ }
915
+
916
+ emitDiag := func (diagnostic tfdiags.Diagnostic ) {
917
+ server .Send (& stacks.MigrateTerraformState_Event {
918
+ Result : & stacks.MigrateTerraformState_Event_Diagnostic {
919
+ Diagnostic : diagnosticToProto (diagnostic ),
920
+ },
921
+ })
922
+ }
923
+
924
+ migrate .Migrate (request .ResourceAddressMap , request .ModuleAddressMap , emit , emitDiag )
925
+ return nil
926
+ }
927
+
779
928
func stackPlanHooks (evts * syncPlanStackChangesServer , mainStackSource sourceaddrs.FinalSource ) * stackruntime.Hooks {
780
929
return stackChangeHooks (
781
930
func (scp * stacks.StackChangeProgress ) error {
0 commit comments