Skip to content

Prototyping how new plugin protocol methods fit with pre-existing interfaces used to access state #36932

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 4 commits into
base: radek/pluggable-backends-protocol
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions internal/backend/pluggable-state/main.go
Original file line number Diff line number Diff line change
@@ -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
}
6 changes: 0 additions & 6 deletions internal/builtin/providers/terraform/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
46 changes: 46 additions & 0 deletions internal/builtin/providers/terraform/storage.go
Original file line number Diff line number Diff line change
@@ -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
}
28 changes: 19 additions & 9 deletions internal/command/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}
Expand Down
25 changes: 25 additions & 0 deletions internal/plugin/grpc_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
}
25 changes: 25 additions & 0 deletions internal/plugin6/grpc_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
17 changes: 12 additions & 5 deletions internal/provider-simple-v6/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
},
},
},
},
},
}
}
Expand Down Expand Up @@ -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
}
42 changes: 42 additions & 0 deletions internal/provider-simple-v6/storage.go
Original file line number Diff line number Diff line change
@@ -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{}
}
24 changes: 24 additions & 0 deletions internal/providers/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Loading
Loading