opentofu/terraform/provisioner_mock.go
Martin Atkins 032f908f4a core: MockProvider and MockProvisioner can't lock in Stop
The only reasonable usage of these methods is for them to run concurrently
with other methods, so we mustn't hold a lock to do this work. For tests
that deal with stopping, it's the test's own responsibility to deal with
any concurrency issues that arise from their StopFns running concurrently
with other mock functions.
2018-10-16 19:14:11 -07:00

146 lines
4.0 KiB
Go

package terraform
import (
"sync"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/convert"
"github.com/hashicorp/terraform/provisioners"
)
var _ provisioners.Interface = (*MockProvisioner)(nil)
// MockProvisioner implements provisioners.Interface but mocks out all the
// calls for testing purposes.
type MockProvisioner struct {
sync.Mutex
// Anything you want, in case you need to store extra data with the mock.
Meta interface{}
GetSchemaCalled bool
GetSchemaResponse provisioners.GetSchemaResponse
ValidateProvisionerConfigCalled bool
ValidateProvisionerConfigRequest provisioners.ValidateProvisionerConfigRequest
ValidateProvisionerConfigResponse provisioners.ValidateProvisionerConfigResponse
ValidateProvisionerConfigFn func(provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse
ProvisionResourceCalled bool
ProvisionResourceRequest provisioners.ProvisionResourceRequest
ProvisionResourceResponse provisioners.ProvisionResourceResponse
ProvisionResourceFn func(provisioners.ProvisionResourceRequest) provisioners.ProvisionResourceResponse
StopCalled bool
StopResponse error
StopFn func() error
CloseCalled bool
CloseResponse error
CloseFn func() error
// Legacy callbacks: if these are set, we will shim incoming calls for
// new-style methods to these old-fashioned terraform.ResourceProvider
// mock callbacks, for the benefit of older tests that were written against
// the old mock API.
ApplyFn func(rs *InstanceState, c *ResourceConfig) error
}
func (p *MockProvisioner) GetSchema() provisioners.GetSchemaResponse {
p.Lock()
defer p.Unlock()
p.GetSchemaCalled = true
return p.getSchema()
}
// getSchema is the implementation of GetSchema, which can be called from other
// methods on MockProvisioner that may already be holding the lock.
func (p *MockProvisioner) getSchema() provisioners.GetSchemaResponse {
return p.GetSchemaResponse
}
func (p *MockProvisioner) ValidateProvisionerConfig(r provisioners.ValidateProvisionerConfigRequest) provisioners.ValidateProvisionerConfigResponse {
p.Lock()
defer p.Unlock()
p.ValidateProvisionerConfigCalled = true
p.ValidateProvisionerConfigRequest = r
if p.ValidateProvisionerConfigFn != nil {
return p.ValidateProvisionerConfigFn(r)
}
return p.ValidateProvisionerConfigResponse
}
func (p *MockProvisioner) ProvisionResource(r provisioners.ProvisionResourceRequest) provisioners.ProvisionResourceResponse {
p.Lock()
defer p.Unlock()
p.ProvisionResourceCalled = true
p.ProvisionResourceRequest = r
if p.ApplyFn != nil {
schema := p.getSchema()
rc := NewResourceConfigShimmed(r.Config, schema.Provisioner)
connVal := r.Connection
connMap := map[string]string{}
for it := connVal.ElementIterator(); it.Next(); {
ak, av := it.Element()
name := ak.AsString()
if !av.IsKnown() || av.IsNull() {
continue
}
av, _ = convert.Convert(av, cty.String)
connMap[name] = av.AsString()
}
// We no longer pass the full instance state to a provisioner, so we'll
// construct a partial one that should be good enough for what existing
// test mocks need.
is := &InstanceState{
Ephemeral: EphemeralState{
ConnInfo: connMap,
},
}
var resp provisioners.ProvisionResourceResponse
err := p.ApplyFn(is, rc)
if err != nil {
resp.Diagnostics = resp.Diagnostics.Append(err)
}
return resp
}
if p.ProvisionResourceFn != nil {
fn := p.ProvisionResourceFn
p.Unlock()
return fn(r)
}
return p.ProvisionResourceResponse
}
func (p *MockProvisioner) Stop() error {
// We intentionally don't lock in this one because the whole point of this
// method is to be called concurrently with another operation that can
// be cancelled. The provisioner itself is responsible for handling
// any concurrency concerns in this case.
p.StopCalled = true
if p.StopFn != nil {
return p.StopFn()
}
return p.StopResponse
}
func (p *MockProvisioner) Close() error {
p.Lock()
defer p.Unlock()
p.CloseCalled = true
if p.CloseFn != nil {
return p.CloseFn()
}
return p.CloseResponse
}