4
4
package rpcapi
5
5
6
6
import (
7
+ "bytes"
7
8
"context"
8
9
"fmt"
9
10
"io"
10
11
"time"
11
12
12
13
"github.com/hashicorp/go-slug/sourceaddrs"
13
14
"github.com/hashicorp/go-slug/sourcebundle"
15
+ "github.com/hashicorp/terraform-svchost/disco"
14
16
"go.opentelemetry.io/otel/attribute"
15
17
otelCodes "go.opentelemetry.io/otel/codes"
16
18
"go.opentelemetry.io/otel/trace"
@@ -22,20 +24,25 @@ import (
22
24
"github.com/hashicorp/terraform/internal/plans"
23
25
"github.com/hashicorp/terraform/internal/providercache"
24
26
"github.com/hashicorp/terraform/internal/providers"
27
+ "github.com/hashicorp/terraform/internal/rpcapi/terraform1"
25
28
"github.com/hashicorp/terraform/internal/rpcapi/terraform1/stacks"
26
29
"github.com/hashicorp/terraform/internal/stacks/stackaddrs"
27
30
"github.com/hashicorp/terraform/internal/stacks/stackconfig"
31
+ "github.com/hashicorp/terraform/internal/stacks/stackmigrate"
28
32
"github.com/hashicorp/terraform/internal/stacks/stackplan"
29
33
"github.com/hashicorp/terraform/internal/stacks/stackruntime"
30
34
"github.com/hashicorp/terraform/internal/stacks/stackruntime/hooks"
31
35
"github.com/hashicorp/terraform/internal/stacks/stackstate"
36
+ "github.com/hashicorp/terraform/internal/states"
37
+ "github.com/hashicorp/terraform/internal/states/statefile"
32
38
"github.com/hashicorp/terraform/internal/tfdiags"
33
39
)
34
40
35
41
type stacksServer struct {
36
42
stacks.UnimplementedStacksServer
37
43
38
44
stopper * stopper
45
+ services * disco.Disco
39
46
handles * handleTable
40
47
experimentsAllowed bool
41
48
@@ -53,11 +60,16 @@ type stacksServer struct {
53
60
planTimestampOverride * time.Time
54
61
}
55
62
56
- var _ stacks.StacksServer = (* stacksServer )(nil )
63
+ var (
64
+ _ stacks.StacksServer = (* stacksServer )(nil )
57
65
58
- func newStacksServer (stopper * stopper , handles * handleTable , opts * serviceOpts ) * stacksServer {
66
+ WorkspaceNameEnvVar = "TF_WORKSPACE"
67
+ )
68
+
69
+ func newStacksServer (stopper * stopper , handles * handleTable , services * disco.Disco , opts * serviceOpts ) * stacksServer {
59
70
return & stacksServer {
60
71
stopper : stopper ,
72
+ services : services ,
61
73
handles : handles ,
62
74
experimentsAllowed : opts .experimentsAllowed ,
63
75
}
@@ -860,6 +872,140 @@ func (s *stacksServer) InspectExpressionResult(ctx context.Context, req *stacks.
860
872
return insp .InspectExpressionResult (ctx , req )
861
873
}
862
874
875
+ func (s * stacksServer ) OpenTerraformState (ctx context.Context , request * stacks.OpenTerraformState_Request ) (* stacks.OpenTerraformState_Response , error ) {
876
+ switch data := request .State .(type ) {
877
+ case * stacks.OpenTerraformState_Request_ConfigPath :
878
+ // Load the state from the backend.
879
+ // This function should return an empty state even if the diags
880
+ // has errors. This makes it easier for the caller, as they should
881
+ // close the state handle regardless of the diags.
882
+ loader := stackmigrate.Loader {Discovery : s .services }
883
+ state , diags := loader .LoadState (data .ConfigPath )
884
+
885
+ hnd := s .handles .NewTerraformState (state )
886
+ return & stacks.OpenTerraformState_Response {
887
+ StateHandle : hnd .ForProtobuf (),
888
+ Diagnostics : diagnosticsToProto (diags ),
889
+ }, nil
890
+
891
+ case * stacks.OpenTerraformState_Request_Raw :
892
+ // load the state from the raw data
893
+ file , err := statefile .Read (bytes .NewReader (data .Raw ))
894
+ if err != nil {
895
+ return nil , status .Errorf (codes .InvalidArgument , "invalid raw state data: %s" , err )
896
+ }
897
+
898
+ hnd := s .handles .NewTerraformState (file .State )
899
+ return & stacks.OpenTerraformState_Response {
900
+ StateHandle : hnd .ForProtobuf (),
901
+ }, nil
902
+
903
+ default :
904
+ return nil , status .Error (codes .InvalidArgument , "invalid state source" )
905
+ }
906
+ }
907
+
908
+ func (s * stacksServer ) CloseTerraformState (ctx context.Context , request * stacks.CloseTerraformState_Request ) (* stacks.CloseTerraformState_Response , error ) {
909
+ hnd := handle [* states.State ](request .StateHandle )
910
+ err := s .handles .CloseTerraformState (hnd )
911
+ if err != nil {
912
+ return nil , status .Error (codes .InvalidArgument , err .Error ())
913
+ }
914
+ return new (stacks.CloseTerraformState_Response ), nil
915
+ }
916
+
917
+ func (s * stacksServer ) MigrateTerraformState (request * stacks.MigrateTerraformState_Request , server stacks.Stacks_MigrateTerraformStateServer ) error {
918
+
919
+ previousStateHandle := handle [* states.State ](request .StateHandle )
920
+ previousState := s .handles .TerraformState (previousStateHandle )
921
+ if previousState == nil {
922
+ return status .Error (codes .InvalidArgument , "the given state handle is invalid" )
923
+ }
924
+
925
+ configHandle := handle [* stackconfig.Config ](request .ConfigHandle )
926
+ config := s .handles .StackConfig (configHandle )
927
+ if config == nil {
928
+ return status .Error (codes .InvalidArgument , "the given config handle is invalid" )
929
+ }
930
+
931
+ dependencyLocksHandle := handle [* depsfile.Locks ](request .DependencyLocksHandle )
932
+ dependencyLocks := s .handles .DependencyLocks (dependencyLocksHandle )
933
+ if dependencyLocks == nil {
934
+ return status .Error (codes .InvalidArgument , "the given dependency locks handle is invalid" )
935
+ }
936
+
937
+ var providerFactories map [addrs.Provider ]providers.Factory
938
+ if s .providerCacheOverride != nil {
939
+ // This is only used in tests to side load providers without needing a
940
+ // real provider cache.
941
+ providerFactories = s .providerCacheOverride
942
+ } else {
943
+ providerCacheHandle := handle [* providercache.Dir ](request .ProviderCacheHandle )
944
+ providerCache := s .handles .ProviderPluginCache (providerCacheHandle )
945
+ if providerCache == nil {
946
+ return status .Error (codes .InvalidArgument , "the given provider cache handle is invalid" )
947
+ }
948
+
949
+ var err error
950
+ providerFactories , err = providerFactoriesForLocks (dependencyLocks , providerCache )
951
+ if err != nil {
952
+ return status .Errorf (codes .InvalidArgument , "provider dependencies are inconsistent: %s" , err )
953
+ }
954
+ }
955
+
956
+ migrate := & stackmigrate.Migration {
957
+ Providers : providerFactories ,
958
+ PreviousState : previousState ,
959
+ Config : config ,
960
+ }
961
+
962
+ emit := func (change stackstate.AppliedChange ) {
963
+ proto , err := change .AppliedChangeProto ()
964
+ if err != nil {
965
+ server .Send (& stacks.MigrateTerraformState_Event {
966
+ Result : & stacks.MigrateTerraformState_Event_Diagnostic {
967
+ Diagnostic : & terraform1.Diagnostic {
968
+ Severity : terraform1 .Diagnostic_ERROR ,
969
+ Summary : "Failed to serialize change" ,
970
+ Detail : fmt .Sprintf ("Failed to serialize state change for recording in the migration plan: %s" , err ),
971
+ },
972
+ },
973
+ })
974
+ return
975
+ }
976
+
977
+ server .Send (& stacks.MigrateTerraformState_Event {
978
+ Result : & stacks.MigrateTerraformState_Event_AppliedChange {
979
+ AppliedChange : proto ,
980
+ },
981
+ })
982
+ }
983
+
984
+ emitDiag := func (diagnostic tfdiags.Diagnostic ) {
985
+ server .Send (& stacks.MigrateTerraformState_Event {
986
+ Result : & stacks.MigrateTerraformState_Event_Diagnostic {
987
+ Diagnostic : diagnosticToProto (diagnostic ),
988
+ },
989
+ })
990
+ }
991
+
992
+ mapping := request .GetMapping ()
993
+ if mapping == nil {
994
+ return status .Error (codes .InvalidArgument , "missing migration mapping" )
995
+ }
996
+ switch mapping := mapping .(type ) {
997
+ case * stacks.MigrateTerraformState_Request_Simple :
998
+ migrate .Migrate (
999
+ mapping .Simple .ResourceAddressMap ,
1000
+ mapping .Simple .ModuleAddressMap ,
1001
+ emit , emitDiag )
1002
+ default :
1003
+ return status .Error (codes .InvalidArgument , "unsupported migration mapping" )
1004
+ }
1005
+
1006
+ return nil
1007
+ }
1008
+
863
1009
func stackPlanHooks (evts * syncPlanStackChangesServer , mainStackSource sourceaddrs.FinalSource ) * stackruntime.Hooks {
864
1010
return stackChangeHooks (
865
1011
func (scp * stacks.StackChangeProgress ) error {
0 commit comments