diff --git a/internal/backend/pluggable-state/main.go b/internal/backend/pluggable-state/main.go new file mode 100644 index 000000000000..078a9083ad08 --- /dev/null +++ b/internal/backend/pluggable-state/main.go @@ -0,0 +1,68 @@ +package pluggable_state + +import ( + "github.com/hashicorp/terraform/internal/backend" + "github.com/hashicorp/terraform/internal/configs/configschema" + "github.com/hashicorp/terraform/internal/providers" + grpc_statemgr "github.com/hashicorp/terraform/internal/states/grpc" + "github.com/hashicorp/terraform/internal/states/statemgr" + "github.com/hashicorp/terraform/internal/tfdiags" + "github.com/zclconf/go-cty/cty" +) + +func NewPluggable(p providers.Interface, typeName string) backend.Backend { + return &Pluggable{ + provider: p, + typeName: typeName, + } +} + +var _ backend.Backend = &Pluggable{} + +type Pluggable struct { + provider providers.Interface + typeName string +} + +func (p *Pluggable) ConfigSchema() *configschema.Block { + schemaResp := p.provider.GetProviderSchema() + if schemaResp.StateStores == nil { + // No state stores + return nil + } + val, ok := schemaResp.StateStores[p.typeName] + if !ok { + // Cannot find state store with that type + return nil + } + + // State store type exists + return val.Body +} + +func (p *Pluggable) PrepareConfig(config cty.Value) (cty.Value, tfdiags.Diagnostics) { + req := providers.ValidateStorageConfigRequest{ + TypeName: p.typeName, + Config: config, + } + resp := p.provider.ValidateStorageConfig(req) + return config, resp.Diagnostics +} + +func (p *Pluggable) Configure(config cty.Value) tfdiags.Diagnostics { + return nil +} + +func (p *Pluggable) Workspaces() ([]string, error) { + return nil, nil +} + +func (p *Pluggable) DeleteWorkspace(workspace string, force bool) error { + return nil +} + +func (p *Pluggable) StateMgr(workspace string) (statemgr.Full, error) { + // repackages the provider's methods inside a state manager, + // to be passed to the calling code that expects a statemgr.Full + return grpc_statemgr.NewGrpcStateManager(p.provider, p.typeName, workspace), nil +} diff --git a/internal/builtin/providers/terraform/provider.go b/internal/builtin/providers/terraform/provider.go index a135cad6c41a..45992434db2c 100644 --- a/internal/builtin/providers/terraform/provider.go +++ b/internal/builtin/providers/terraform/provider.go @@ -269,9 +269,3 @@ func (p *Provider) CallFunction(req providers.CallFunctionRequest) providers.Cal func (p *Provider) Close() error { return nil } - -func (p *Provider) ValidateStorageConfig(req providers.ValidateStorageConfigRequest) providers.ValidateStorageConfigResponse { - var resp providers.ValidateStorageConfigResponse - resp.Diagnostics.Append(fmt.Errorf("unsupported storage type %q", req.TypeName)) - return resp -} diff --git a/internal/builtin/providers/terraform/storage.go b/internal/builtin/providers/terraform/storage.go new file mode 100644 index 000000000000..101fe27a4bde --- /dev/null +++ b/internal/builtin/providers/terraform/storage.go @@ -0,0 +1,46 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package terraform + +import ( + "fmt" + + "github.com/hashicorp/terraform/internal/providers" +) + +func (p *Provider) ValidateStorageConfig(req providers.ValidateStorageConfigRequest) providers.ValidateStorageConfigResponse { + var resp providers.ValidateStorageConfigResponse + resp.Diagnostics.Append(fmt.Errorf("unsupported storage type %q", req.TypeName)) + return resp +} + +func (p *Provider) ConfigureStorage(req providers.ConfigureStorageRequest) providers.ConfigureStorageResponse { + var resp providers.ConfigureStorageResponse + resp.Diagnostics.Append(fmt.Errorf("unsupported storage type %q", req.TypeName)) + return resp +} + +func (p *Provider) LockState(req providers.LockStateRequest) providers.LockStateResponse { + var resp providers.LockStateResponse + resp.Diagnostics.Append(fmt.Errorf("unsupported storage type %q", req.TypeName)) + return resp +} + +func (p *Provider) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + var resp providers.UnlockStateResponse + resp.Diagnostics.Append(fmt.Errorf("unsupported storage type %q", req.TypeName)) + return resp +} + +func (p *Provider) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + var resp providers.GetStatesResponse + resp.StateIds = nil // No states when no state storage is implemented, even `default` + return resp +} + +func (p *Provider) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + var resp providers.DeleteStateResponse + resp.Diagnostics.Append(fmt.Errorf("unsupported storage type %q", req.TypeName)) + return resp +} diff --git a/internal/command/init.go b/internal/command/init.go index 4ba9448edfdc..1c485682c3dc 100644 --- a/internal/command/init.go +++ b/internal/command/init.go @@ -23,12 +23,15 @@ import ( "github.com/hashicorp/terraform/internal/addrs" "github.com/hashicorp/terraform/internal/backend" backendInit "github.com/hashicorp/terraform/internal/backend/init" + "github.com/hashicorp/terraform/internal/backend/local" + pluggable_state "github.com/hashicorp/terraform/internal/backend/pluggable-state" "github.com/hashicorp/terraform/internal/cloud" "github.com/hashicorp/terraform/internal/command/arguments" "github.com/hashicorp/terraform/internal/command/views" "github.com/hashicorp/terraform/internal/configs" "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/getproviders" + simple "github.com/hashicorp/terraform/internal/provider-simple-v6" "github.com/hashicorp/terraform/internal/providercache" "github.com/hashicorp/terraform/internal/states" "github.com/hashicorp/terraform/internal/terraform" @@ -180,15 +183,22 @@ func (c *InitCommand) Run(args []string) int { var backDiags tfdiags.Diagnostics var backendOutput bool - switch { - case initArgs.Cloud && rootModEarly.CloudConfig != nil: - back, backendOutput, backDiags = c.initCloud(ctx, rootModEarly, initArgs.BackendConfig, initArgs.ViewType, view) - case initArgs.Backend: - back, backendOutput, backDiags = c.initBackend(ctx, rootModEarly, initArgs.BackendConfig, initArgs.ViewType, view) - default: - // load the previously-stored backend config - back, backDiags = c.Meta.backendFromState(ctx) - } + spv6 := simple.Provider() + // Which state storage implementation to use from the provider + typeName := "local" + // How do we convert a provider into a Backend interface? + stateStorage := pluggable_state.NewPluggable(spv6, typeName) + back = local.NewWithBackend(stateStorage) + + // switch { + // case initArgs.Cloud && rootModEarly.CloudConfig != nil: + // back, backendOutput, backDiags = c.initCloud(ctx, rootModEarly, initArgs.BackendConfig, initArgs.ViewType, view) + // case initArgs.Backend: + // back, backendOutput, backDiags = c.initBackend(ctx, rootModEarly, initArgs.BackendConfig, initArgs.ViewType, view) + // default: + // // load the previously-stored backend config + // back, backDiags = c.Meta.backendFromState(ctx) + // } if backendOutput { header = true } diff --git a/internal/plugin/grpc_provider.go b/internal/plugin/grpc_provider.go index 4d18315c44df..34cb625fad8c 100644 --- a/internal/plugin/grpc_provider.go +++ b/internal/plugin/grpc_provider.go @@ -1259,3 +1259,28 @@ func (p *GRPCProvider) ValidateStorageConfig(r providers.ValidateStorageConfigRe // TODO return providers.ValidateStorageConfigResponse{} } + +func (p *GRPCProvider) ConfigureStorage(r providers.ConfigureStorageRequest) providers.ConfigureStorageResponse { + // TODO + return providers.ConfigureStorageResponse{} +} + +func (p *GRPCProvider) LockState(req providers.LockStateRequest) providers.LockStateResponse { + // TODO + return providers.LockStateResponse{} +} + +func (p *GRPCProvider) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + // TODO + return providers.UnlockStateResponse{} +} + +func (p *GRPCProvider) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + // TODO + return providers.GetStatesResponse{} +} + +func (p *GRPCProvider) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + // TODO + return providers.DeleteStateResponse{} +} diff --git a/internal/plugin6/grpc_provider.go b/internal/plugin6/grpc_provider.go index 9d681a67cfac..c3ae809103fa 100644 --- a/internal/plugin6/grpc_provider.go +++ b/internal/plugin6/grpc_provider.go @@ -1203,6 +1203,31 @@ func (p *GRPCProvider) ValidateStorageConfig(r providers.ValidateStorageConfigRe return providers.ValidateStorageConfigResponse{} } +func (p *GRPCProvider) ConfigureStorage(r providers.ConfigureStorageRequest) providers.ConfigureStorageResponse { + // TODO + return providers.ConfigureStorageResponse{} +} + +func (p *GRPCProvider) LockState(req providers.LockStateRequest) providers.LockStateResponse { + // TODO + return providers.LockStateResponse{} +} + +func (p *GRPCProvider) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + // TODO + return providers.UnlockStateResponse{} +} + +func (p *GRPCProvider) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + // TODO + return providers.GetStatesResponse{} +} + +func (p *GRPCProvider) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + // TODO + return providers.DeleteStateResponse{} +} + // closing the grpc connection is final, and terraform will call it at the end of every phase. func (p *GRPCProvider) Close() error { logger.Trace("GRPCProvider.v6: Close") diff --git a/internal/provider-simple-v6/provider.go b/internal/provider-simple-v6/provider.go index ee85553b1652..ac6f31c822c1 100644 --- a/internal/provider-simple-v6/provider.go +++ b/internal/provider-simple-v6/provider.go @@ -74,6 +74,18 @@ func Provider() providers.Interface { DescriptionKind: configschema.StringPlain, }, }, + StateStores: map[string]providers.Schema{ + "local": { + Body: &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "path": { + Computed: true, + Type: cty.String, + }, + }, + }, + }, + }, }, } } @@ -249,11 +261,6 @@ func (s simple) CallFunction(req providers.CallFunctionRequest) (resp providers. return resp } -func (s simple) ValidateStorageConfig(req providers.ValidateStorageConfigRequest) providers.ValidateStorageConfigResponse { - // TODO - return providers.ValidateStorageConfigResponse{} -} - func (s simple) Close() error { return nil } diff --git a/internal/provider-simple-v6/storage.go b/internal/provider-simple-v6/storage.go new file mode 100644 index 000000000000..c6fc7f1e7cce --- /dev/null +++ b/internal/provider-simple-v6/storage.go @@ -0,0 +1,42 @@ +package simple + +import ( + "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/tfdiags" +) + +func (s simple) ValidateStorageConfig(req providers.ValidateStorageConfigRequest) providers.ValidateStorageConfigResponse { + // TODO + var diags tfdiags.Diagnostics + return providers.ValidateStorageConfigResponse{ + Diagnostics: diags, + } +} + +func (s simple) ConfigureStorage(req providers.ConfigureStorageRequest) providers.ConfigureStorageResponse { + // TODO + var diags tfdiags.Diagnostics + return providers.ConfigureStorageResponse{ + Diagnostics: diags, + } +} + +func (s simple) LockState(req providers.LockStateRequest) providers.LockStateResponse { + // TODO + return providers.LockStateResponse{} +} + +func (s simple) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + // TODO + return providers.UnlockStateResponse{} +} + +func (s simple) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + // TODO + return providers.GetStatesResponse{} +} + +func (s simple) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + // TODO + return providers.DeleteStateResponse{} +} diff --git a/internal/providers/mock.go b/internal/providers/mock.go index 6c9c3b1bd7f6..7545e2893c83 100644 --- a/internal/providers/mock.go +++ b/internal/providers/mock.go @@ -410,6 +410,30 @@ func (m *Mock) ValidateStorageConfig(req ValidateStorageConfigRequest) ValidateS return m.Provider.ValidateStorageConfig(req) } +func (m *Mock) ConfigureStorage(req ConfigureStorageRequest) ConfigureStorageResponse { + return m.Provider.ConfigureStorage(req) +} + +func (m *Mock) LockState(req LockStateRequest) LockStateResponse { + // TODO + return LockStateResponse{} +} + +func (m *Mock) UnlockState(req UnlockStateRequest) UnlockStateResponse { + // TODO + return UnlockStateResponse{} +} + +func (m *Mock) GetStates(req GetStatesRequest) GetStatesResponse { + // TODO + return GetStatesResponse{} +} + +func (m *Mock) DeleteState(req DeleteStateRequest) DeleteStateResponse { + // TODO + return DeleteStateResponse{} +} + func (m *Mock) Close() error { return m.Provider.Close() } diff --git a/internal/providers/provider.go b/internal/providers/provider.go index 2bf80ca8c01f..ac95e32923b3 100644 --- a/internal/providers/provider.go +++ b/internal/providers/provider.go @@ -102,15 +102,15 @@ type Interface interface { // ValidateStorageConfig allows the provider to validate storage configuration values ValidateStorageConfig(ValidateStorageConfigRequest) ValidateStorageConfigResponse - // ConfigureStorage(ConfigureStorageRequest) ConfigureStorageResponse + ConfigureStorage(ConfigureStorageRequest) ConfigureStorageResponse // ReadState(ReadStateRequest, srv ProviderReadStateServer) // WriteState(srv ProviderWriteStateServer) - // LockState(LockStateRequest) LockStateResponse - // UnlockState(UnlockStateRequest) UnlockStateResponse - // GetStates(GetStatesRequest) GetStatesResponse - // DeleteState(DeleteStateRequest) DeleteStateResponse + LockState(LockStateRequest) LockStateResponse + UnlockState(UnlockStateRequest) UnlockStateResponse + GetStates(GetStatesRequest) GetStatesResponse + DeleteState(DeleteStateRequest) DeleteStateResponse // Close shuts down the plugin process if applicable. Close() error @@ -715,3 +715,74 @@ type ValidateStorageConfigResponse struct { // Diagnostics contains any warnings or errors from the method call. Diagnostics tfdiags.Diagnostics } + +type ConfigureStorageRequest struct { + // TypeName is the name of the storage type to validate. + TypeName string + + // Config is the configuration to use. + Config cty.Value +} + +type ConfigureStorageResponse struct { + // Diagnostics contains any warnings or errors from the method call. + Diagnostics tfdiags.Diagnostics +} + +type LockStateRequest struct { + // TypeName is the name of the storage type to send the lock request to. + TypeName string + + // TypeName is the name of the state (workspace state) to lock. + StateId string + + // Operation is the Terraform operation being performed that requires the lock + Operation string +} + +type LockStateResponse struct { + // LockId is the identifier for the created lock + LockId string + + // Diagnostics contains any warnings or errors from the method call. + Diagnostics tfdiags.Diagnostics +} + +type UnlockStateRequest struct { + // TypeName is the name of the storage type to send the lock request to. + TypeName string + + // TypeName is the name of the state (workspace state) to lock. + StateId string + + // LockId is the identifier for the lock to unlock + LockId string +} + +type UnlockStateResponse struct { + // Diagnostics contains any warnings or errors from the method call. + Diagnostics tfdiags.Diagnostics +} + +type GetStatesRequest struct { + // TypeName is the name of the storage type to send the get states request to. + TypeName string +} + +type GetStatesResponse struct { + // State IDs is a slice of identifiers for states stored in the state store + StateIds []string +} + +type DeleteStateRequest struct { + // TypeName is the name of the storage type to send the delete state request to. + TypeName string + + // TypeName is the name of the state (workspace state) to delete. + StateId string +} + +type DeleteStateResponse struct { + // Diagnostics contains any warnings or errors from the method call. + Diagnostics tfdiags.Diagnostics +} diff --git a/internal/providers/testing/provider_mock.go b/internal/providers/testing/provider_mock.go index e19c701aae2b..357c373bf888 100644 --- a/internal/providers/testing/provider_mock.go +++ b/internal/providers/testing/provider_mock.go @@ -830,6 +830,46 @@ func (p *MockProvider) ValidateStorageConfig(r providers.ValidateStorageConfigRe return resp } +func (p *MockProvider) ConfigureStorage(r providers.ConfigureStorageRequest) (resp providers.ConfigureStorageResponse) { + p.Lock() + defer p.Unlock() + + //TODO + return resp +} + +func (p *MockProvider) LockState(req providers.LockStateRequest) providers.LockStateResponse { + p.Lock() + defer p.Unlock() + + // TODO + return providers.LockStateResponse{} +} + +func (p *MockProvider) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + p.Lock() + defer p.Unlock() + + // TODO + return providers.UnlockStateResponse{} +} + +func (p *MockProvider) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + p.Lock() + defer p.Unlock() + + // TODO + return providers.GetStatesResponse{} +} + +func (p *MockProvider) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + p.Lock() + defer p.Unlock() + + // TODO + return providers.DeleteStateResponse{} +} + func (p *MockProvider) Close() error { p.Lock() defer p.Unlock() diff --git a/internal/refactoring/mock_provider.go b/internal/refactoring/mock_provider.go index acb4e9ae0ce7..5186575f9392 100644 --- a/internal/refactoring/mock_provider.go +++ b/internal/refactoring/mock_provider.go @@ -8,7 +8,7 @@ import ( "github.com/hashicorp/terraform/internal/tfdiags" ) -var _ providers.Interface = (*mockProvider)(nil) +// var _ providers.Interface = (*mockProvider)(nil) // mockProvider provides a mock implementation of providers.Interface that only // implements the methods that are used by the refactoring package. diff --git a/internal/stacks/stackruntime/internal/stackeval/stubs/errored.go b/internal/stacks/stackruntime/internal/stackeval/stubs/errored.go index c482a278464c..5ae8f34452ee 100644 --- a/internal/stacks/stackruntime/internal/stackeval/stubs/errored.go +++ b/internal/stacks/stackruntime/internal/stackeval/stubs/errored.go @@ -239,7 +239,31 @@ func (p *erroredProvider) ValidateResourceConfig(providers.ValidateResourceConfi } func (p *erroredProvider) ValidateStorageConfig(req providers.ValidateStorageConfigRequest) providers.ValidateStorageConfigResponse { - return providers.ValidateStorageConfigResponse{ - Diagnostics: nil, - } + // TODO + return providers.ValidateStorageConfigResponse{} +} + +func (p *erroredProvider) ConfigureStorage(req providers.ConfigureStorageRequest) providers.ConfigureStorageResponse { + // TODO + return providers.ConfigureStorageResponse{} +} + +func (p *erroredProvider) LockState(req providers.LockStateRequest) providers.LockStateResponse { + // TODO + return providers.LockStateResponse{} +} + +func (p *erroredProvider) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + // TODO + return providers.UnlockStateResponse{} +} + +func (p *erroredProvider) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + // TODO + return providers.GetStatesResponse{} +} + +func (p *erroredProvider) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + // TODO + return providers.DeleteStateResponse{} } diff --git a/internal/stacks/stackruntime/internal/stackeval/stubs/offline.go b/internal/stacks/stackruntime/internal/stackeval/stubs/offline.go index 300f7791fc74..5a08f0494fbc 100644 --- a/internal/stacks/stackruntime/internal/stackeval/stubs/offline.go +++ b/internal/stacks/stackruntime/internal/stackeval/stubs/offline.go @@ -248,6 +248,39 @@ func (o *offlineProvider) ValidateStorageConfig(req providers.ValidateStorageCon } } +func (o *offlineProvider) ConfigureStorage(req providers.ConfigureStorageRequest) providers.ConfigureStorageResponse { + var diags tfdiags.Diagnostics + diags = diags.Append(tfdiags.AttributeValue( + tfdiags.Error, + "Called ConfigureStorage on an unconfigured provider", + "Cannot validate storage configuration because this provider is not configured. This is a bug in Terraform - please report it.", + nil, // nil attribute path means the overall configuration block + )) + return providers.ConfigureStorageResponse{ + Diagnostics: diags, + } +} + +func (o *offlineProvider) LockState(req providers.LockStateRequest) providers.LockStateResponse { + // TODO + return providers.LockStateResponse{} +} + +func (o *offlineProvider) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + // TODO + return providers.UnlockStateResponse{} +} + +func (o *offlineProvider) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + // TODO + return providers.GetStatesResponse{} +} + +func (o *offlineProvider) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + // TODO + return providers.DeleteStateResponse{} +} + func (o *offlineProvider) Close() error { // pass the close call to the underlying unconfigured client return o.unconfiguredClient.Close() diff --git a/internal/stacks/stackruntime/internal/stackeval/stubs/unknown.go b/internal/stacks/stackruntime/internal/stackeval/stubs/unknown.go index 90c7d950c4f3..ca5792618ed8 100644 --- a/internal/stacks/stackruntime/internal/stackeval/stubs/unknown.go +++ b/internal/stacks/stackruntime/internal/stackeval/stubs/unknown.go @@ -14,7 +14,7 @@ import ( "github.com/hashicorp/terraform/internal/tfdiags" ) -var _ providers.Interface = (*unknownProvider)(nil) +// var _ providers.Interface = (*unknownProvider)(nil) // unknownProvider is a stub provider that represents a provider that is // unknown to the current Terraform configuration. This is used when a reference @@ -292,9 +292,39 @@ func (u *unknownProvider) CallFunction(_ providers.CallFunctionRequest) provider } func (u *unknownProvider) ValidateStorageConfig(request providers.ValidateStorageConfigRequest) providers.ValidateStorageConfigResponse { - // This is offline functionality, so we can hand it off to the unconfigured - // client. - return u.unconfiguredClient.ValidateStorageConfig(request) + // This shouldn't be called, we don't configure state storage in an unknown provider within + // stacks and Terraform Core shouldn't call this method. + panic("attempted to validate configuration for state storage in an unknown provider") +} + +func (u *unknownProvider) ConfigureStorage(request providers.ConfigureStorageRequest) providers.ConfigureStorageResponse { + // This shouldn't be called, we don't configure state storage in an unknown provider within + // stacks and Terraform Core shouldn't call this method. + panic("attempted to configure state storage in an unknown provider") +} + +func (u *unknownProvider) LockState(req providers.LockStateRequest) providers.LockStateResponse { + // This shouldn't be called, an unknown provider should not be used to manage state within + // stacks and Terraform Core shouldn't call this method. + panic("attempted to lock state via an unknown provider") +} + +func (u *unknownProvider) UnlockState(req providers.UnlockStateRequest) providers.UnlockStateResponse { + // This shouldn't be called, an unknown provider should not be used to manage state within + // stacks and Terraform Core shouldn't call this method. + panic("attempted to unlock state via an unknown provider") +} + +func (u *unknownProvider) GetStates(req providers.GetStatesRequest) providers.GetStatesResponse { + // This shouldn't be called, an unknown provider should not be used to manage state within + // stacks and Terraform Core shouldn't call this method. + panic("attempted to get states via an unknown provider") +} + +func (u *unknownProvider) DeleteState(req providers.DeleteStateRequest) providers.DeleteStateResponse { + // This shouldn't be called, an unknown provider should not be used to manage state within + // stacks and Terraform Core shouldn't call this method. + panic("attempted to delete a state via an unknown provider") } func (u *unknownProvider) Close() error { diff --git a/internal/states/grpc/main.go b/internal/states/grpc/main.go new file mode 100644 index 000000000000..237dce78efc3 --- /dev/null +++ b/internal/states/grpc/main.go @@ -0,0 +1,102 @@ +package grpc_statemgr + +import ( + "context" + "fmt" + "sync" + + "github.com/hashicorp/terraform/internal/providers" + "github.com/hashicorp/terraform/internal/schemarepo" + "github.com/hashicorp/terraform/internal/states" + "github.com/hashicorp/terraform/internal/states/statemgr" +) + +var ( + _ statemgr.Full = &grpcStateManager{} +) + +// NewGrpcStateManager takes in a provider that implements states storage +// and returns a state manager implementation that allows calling code to +// use the provider's state management-related methods. +// +// Requires being passed: +// 1) the name of the state storage implementation +// 2) the name of the state/the active workspace +func NewGrpcStateManager(provider providers.Interface, typeName string, stateId string) statemgr.Full { + return &grpcStateManager{ + provider: provider, + typeName: typeName, + stateId: stateId, + } +} + +type grpcStateManager struct { + provider providers.Interface + typeName string // the state storage implementation's name + stateId string + + mu sync.Mutex + // TODO + // We need fields here similar to the remote state manager; we need to + // have in memory state that can be updated repeatedly and eventually persisted + // via a call to PersistState. +} + +func (g *grpcStateManager) Lock(info *statemgr.LockInfo) (string, error) { + req := providers.LockStateRequest{ + TypeName: g.typeName, + StateId: g.stateId, + Operation: info.Operation, + } + resp := g.provider.LockState(req) + return resp.LockId, resp.Diagnostics.Err() +} + +func (g *grpcStateManager) Unlock(id string) error { + req := providers.UnlockStateRequest{ + TypeName: g.typeName, + StateId: g.stateId, + LockId: id, + } + resp := g.provider.UnlockState(req) + return resp.Diagnostics.Err() +} + +func (g *grpcStateManager) State() *states.State { + g.mu.Lock() + defer g.mu.Unlock() + + // TODO: Return a deep copy of the state value from internal, in-memory store here + return nil +} + +func (g *grpcStateManager) WriteState(state *states.State) error { + g.mu.Lock() + defer g.mu.Unlock() + + // TODO: write to internal, in-memory store here + return nil +} + +func (g *grpcStateManager) RefreshState() error { + // No ReadState method implemented on the provider yet + return nil +} + +func (g *grpcStateManager) PersistState(foobar *schemarepo.Schemas) error { + // No WriteState method implemented on the provider yet + return nil +} + +func (g *grpcStateManager) GetRootOutputValues(ctx context.Context) (map[string]*states.OutputValue, error) { + if err := g.RefreshState(); err != nil { + return nil, fmt.Errorf("Failed to load state: %s", err) + } + + state := g.State() + if state == nil { + state = states.NewState() + } + + return state.RootOutputValues, nil +}