github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/terraform/provider_mock.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/zclconf/go-cty/cty"
     8  	ctyjson "github.com/zclconf/go-cty/cty/json"
     9  	"github.com/zclconf/go-cty/cty/msgpack"
    10  
    11  	"github.com/hashicorp/terraform/internal/configs/configschema"
    12  	"github.com/hashicorp/terraform/internal/configs/hcl2shim"
    13  	"github.com/hashicorp/terraform/internal/providers"
    14  )
    15  
    16  var _ providers.Interface = (*MockProvider)(nil)
    17  
    18  // MockProvider implements providers.Interface but mocks out all the
    19  // calls for testing purposes.
    20  type MockProvider struct {
    21  	sync.Mutex
    22  
    23  	// Anything you want, in case you need to store extra data with the mock.
    24  	Meta interface{}
    25  
    26  	GetProviderSchemaCalled   bool
    27  	GetProviderSchemaResponse *providers.GetProviderSchemaResponse
    28  
    29  	ValidateProviderConfigCalled   bool
    30  	ValidateProviderConfigResponse *providers.ValidateProviderConfigResponse
    31  	ValidateProviderConfigRequest  providers.ValidateProviderConfigRequest
    32  	ValidateProviderConfigFn       func(providers.ValidateProviderConfigRequest) providers.ValidateProviderConfigResponse
    33  
    34  	ValidateResourceConfigCalled   bool
    35  	ValidateResourceConfigTypeName string
    36  	ValidateResourceConfigResponse *providers.ValidateResourceConfigResponse
    37  	ValidateResourceConfigRequest  providers.ValidateResourceConfigRequest
    38  	ValidateResourceConfigFn       func(providers.ValidateResourceConfigRequest) providers.ValidateResourceConfigResponse
    39  
    40  	ValidateDataResourceConfigCalled   bool
    41  	ValidateDataResourceConfigTypeName string
    42  	ValidateDataResourceConfigResponse *providers.ValidateDataResourceConfigResponse
    43  	ValidateDataResourceConfigRequest  providers.ValidateDataResourceConfigRequest
    44  	ValidateDataResourceConfigFn       func(providers.ValidateDataResourceConfigRequest) providers.ValidateDataResourceConfigResponse
    45  
    46  	UpgradeResourceStateCalled   bool
    47  	UpgradeResourceStateTypeName string
    48  	UpgradeResourceStateResponse *providers.UpgradeResourceStateResponse
    49  	UpgradeResourceStateRequest  providers.UpgradeResourceStateRequest
    50  	UpgradeResourceStateFn       func(providers.UpgradeResourceStateRequest) providers.UpgradeResourceStateResponse
    51  
    52  	ConfigureProviderCalled   bool
    53  	ConfigureProviderResponse *providers.ConfigureProviderResponse
    54  	ConfigureProviderRequest  providers.ConfigureProviderRequest
    55  	ConfigureProviderFn       func(providers.ConfigureProviderRequest) providers.ConfigureProviderResponse
    56  
    57  	StopCalled   bool
    58  	StopFn       func() error
    59  	StopResponse error
    60  
    61  	ReadResourceCalled   bool
    62  	ReadResourceResponse *providers.ReadResourceResponse
    63  	ReadResourceRequest  providers.ReadResourceRequest
    64  	ReadResourceFn       func(providers.ReadResourceRequest) providers.ReadResourceResponse
    65  
    66  	PlanResourceChangeCalled   bool
    67  	PlanResourceChangeResponse *providers.PlanResourceChangeResponse
    68  	PlanResourceChangeRequest  providers.PlanResourceChangeRequest
    69  	PlanResourceChangeFn       func(providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse
    70  
    71  	ApplyResourceChangeCalled   bool
    72  	ApplyResourceChangeResponse *providers.ApplyResourceChangeResponse
    73  	ApplyResourceChangeRequest  providers.ApplyResourceChangeRequest
    74  	ApplyResourceChangeFn       func(providers.ApplyResourceChangeRequest) providers.ApplyResourceChangeResponse
    75  
    76  	ImportResourceStateCalled   bool
    77  	ImportResourceStateResponse *providers.ImportResourceStateResponse
    78  	ImportResourceStateRequest  providers.ImportResourceStateRequest
    79  	ImportResourceStateFn       func(providers.ImportResourceStateRequest) providers.ImportResourceStateResponse
    80  
    81  	ReadDataSourceCalled   bool
    82  	ReadDataSourceResponse *providers.ReadDataSourceResponse
    83  	ReadDataSourceRequest  providers.ReadDataSourceRequest
    84  	ReadDataSourceFn       func(providers.ReadDataSourceRequest) providers.ReadDataSourceResponse
    85  
    86  	CloseCalled bool
    87  	CloseError  error
    88  }
    89  
    90  func (p *MockProvider) GetProviderSchema() providers.GetProviderSchemaResponse {
    91  	p.Lock()
    92  	defer p.Unlock()
    93  	p.GetProviderSchemaCalled = true
    94  	return p.getProviderSchema()
    95  }
    96  
    97  func (p *MockProvider) getProviderSchema() providers.GetProviderSchemaResponse {
    98  	// This version of getProviderSchema doesn't do any locking, so it's suitable to
    99  	// call from other methods of this mock as long as they are already
   100  	// holding the lock.
   101  	if p.GetProviderSchemaResponse != nil {
   102  		return *p.GetProviderSchemaResponse
   103  	}
   104  
   105  	return providers.GetProviderSchemaResponse{
   106  		Provider:      providers.Schema{},
   107  		DataSources:   map[string]providers.Schema{},
   108  		ResourceTypes: map[string]providers.Schema{},
   109  	}
   110  }
   111  
   112  // ProviderSchema is a helper to convert from the internal GetProviderSchemaResponse to
   113  // a ProviderSchema.
   114  func (p *MockProvider) ProviderSchema() *ProviderSchema {
   115  	resp := p.getProviderSchema()
   116  
   117  	schema := &ProviderSchema{
   118  		Provider:                   resp.Provider.Block,
   119  		ProviderMeta:               resp.ProviderMeta.Block,
   120  		ResourceTypes:              map[string]*configschema.Block{},
   121  		DataSources:                map[string]*configschema.Block{},
   122  		ResourceTypeSchemaVersions: map[string]uint64{},
   123  	}
   124  
   125  	for resType, s := range resp.ResourceTypes {
   126  		schema.ResourceTypes[resType] = s.Block
   127  		schema.ResourceTypeSchemaVersions[resType] = uint64(s.Version)
   128  	}
   129  
   130  	for dataSource, s := range resp.DataSources {
   131  		schema.DataSources[dataSource] = s.Block
   132  	}
   133  
   134  	return schema
   135  }
   136  
   137  func (p *MockProvider) ValidateProviderConfig(r providers.ValidateProviderConfigRequest) (resp providers.ValidateProviderConfigResponse) {
   138  	p.Lock()
   139  	defer p.Unlock()
   140  
   141  	p.ValidateProviderConfigCalled = true
   142  	p.ValidateProviderConfigRequest = r
   143  	if p.ValidateProviderConfigFn != nil {
   144  		return p.ValidateProviderConfigFn(r)
   145  	}
   146  
   147  	if p.ValidateProviderConfigResponse != nil {
   148  		return *p.ValidateProviderConfigResponse
   149  	}
   150  
   151  	resp.PreparedConfig = r.Config
   152  	return resp
   153  }
   154  
   155  func (p *MockProvider) ValidateResourceConfig(r providers.ValidateResourceConfigRequest) (resp providers.ValidateResourceConfigResponse) {
   156  	p.Lock()
   157  	defer p.Unlock()
   158  
   159  	p.ValidateResourceConfigCalled = true
   160  	p.ValidateResourceConfigRequest = r
   161  
   162  	// Marshall the value to replicate behavior by the GRPC protocol,
   163  	// and return any relevant errors
   164  	resourceSchema, ok := p.getProviderSchema().ResourceTypes[r.TypeName]
   165  	if !ok {
   166  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName))
   167  		return resp
   168  	}
   169  
   170  	_, err := msgpack.Marshal(r.Config, resourceSchema.Block.ImpliedType())
   171  	if err != nil {
   172  		resp.Diagnostics = resp.Diagnostics.Append(err)
   173  		return resp
   174  	}
   175  
   176  	if p.ValidateResourceConfigFn != nil {
   177  		return p.ValidateResourceConfigFn(r)
   178  	}
   179  
   180  	if p.ValidateResourceConfigResponse != nil {
   181  		return *p.ValidateResourceConfigResponse
   182  	}
   183  
   184  	return resp
   185  }
   186  
   187  func (p *MockProvider) ValidateDataResourceConfig(r providers.ValidateDataResourceConfigRequest) (resp providers.ValidateDataResourceConfigResponse) {
   188  	p.Lock()
   189  	defer p.Unlock()
   190  
   191  	p.ValidateDataResourceConfigCalled = true
   192  	p.ValidateDataResourceConfigRequest = r
   193  
   194  	// Marshall the value to replicate behavior by the GRPC protocol
   195  	dataSchema, ok := p.getProviderSchema().DataSources[r.TypeName]
   196  	if !ok {
   197  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName))
   198  		return resp
   199  	}
   200  	_, err := msgpack.Marshal(r.Config, dataSchema.Block.ImpliedType())
   201  	if err != nil {
   202  		resp.Diagnostics = resp.Diagnostics.Append(err)
   203  		return resp
   204  	}
   205  
   206  	if p.ValidateDataResourceConfigFn != nil {
   207  		return p.ValidateDataResourceConfigFn(r)
   208  	}
   209  
   210  	if p.ValidateDataResourceConfigResponse != nil {
   211  		return *p.ValidateDataResourceConfigResponse
   212  	}
   213  
   214  	return resp
   215  }
   216  
   217  func (p *MockProvider) UpgradeResourceState(r providers.UpgradeResourceStateRequest) (resp providers.UpgradeResourceStateResponse) {
   218  	p.Lock()
   219  	defer p.Unlock()
   220  
   221  	if !p.ConfigureProviderCalled {
   222  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before UpgradeResourceState %q", r.TypeName))
   223  		return resp
   224  	}
   225  
   226  	schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName]
   227  	if !ok {
   228  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName))
   229  		return resp
   230  	}
   231  
   232  	schemaType := schema.Block.ImpliedType()
   233  
   234  	p.UpgradeResourceStateCalled = true
   235  	p.UpgradeResourceStateRequest = r
   236  
   237  	if p.UpgradeResourceStateFn != nil {
   238  		return p.UpgradeResourceStateFn(r)
   239  	}
   240  
   241  	if p.UpgradeResourceStateResponse != nil {
   242  		return *p.UpgradeResourceStateResponse
   243  	}
   244  
   245  	switch {
   246  	case r.RawStateFlatmap != nil:
   247  		v, err := hcl2shim.HCL2ValueFromFlatmap(r.RawStateFlatmap, schemaType)
   248  		if err != nil {
   249  			resp.Diagnostics = resp.Diagnostics.Append(err)
   250  			return resp
   251  		}
   252  		resp.UpgradedState = v
   253  	case len(r.RawStateJSON) > 0:
   254  		v, err := ctyjson.Unmarshal(r.RawStateJSON, schemaType)
   255  
   256  		if err != nil {
   257  			resp.Diagnostics = resp.Diagnostics.Append(err)
   258  			return resp
   259  		}
   260  		resp.UpgradedState = v
   261  	}
   262  
   263  	return resp
   264  }
   265  
   266  func (p *MockProvider) ConfigureProvider(r providers.ConfigureProviderRequest) (resp providers.ConfigureProviderResponse) {
   267  	p.Lock()
   268  	defer p.Unlock()
   269  
   270  	p.ConfigureProviderCalled = true
   271  	p.ConfigureProviderRequest = r
   272  
   273  	if p.ConfigureProviderFn != nil {
   274  		return p.ConfigureProviderFn(r)
   275  	}
   276  
   277  	if p.ConfigureProviderResponse != nil {
   278  		return *p.ConfigureProviderResponse
   279  	}
   280  
   281  	return resp
   282  }
   283  
   284  func (p *MockProvider) Stop() error {
   285  	// We intentionally don't lock in this one because the whole point of this
   286  	// method is to be called concurrently with another operation that can
   287  	// be cancelled.  The provider itself is responsible for handling
   288  	// any concurrency concerns in this case.
   289  
   290  	p.StopCalled = true
   291  	if p.StopFn != nil {
   292  		return p.StopFn()
   293  	}
   294  
   295  	return p.StopResponse
   296  }
   297  
   298  func (p *MockProvider) ReadResource(r providers.ReadResourceRequest) (resp providers.ReadResourceResponse) {
   299  	p.Lock()
   300  	defer p.Unlock()
   301  
   302  	p.ReadResourceCalled = true
   303  	p.ReadResourceRequest = r
   304  
   305  	if !p.ConfigureProviderCalled {
   306  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ReadResource %q", r.TypeName))
   307  		return resp
   308  	}
   309  
   310  	if p.ReadResourceFn != nil {
   311  		return p.ReadResourceFn(r)
   312  	}
   313  
   314  	if p.ReadResourceResponse != nil {
   315  		resp = *p.ReadResourceResponse
   316  
   317  		// Make sure the NewState conforms to the schema.
   318  		// This isn't always the case for the existing tests.
   319  		schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName]
   320  		if !ok {
   321  			resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName))
   322  			return resp
   323  		}
   324  
   325  		newState, err := schema.Block.CoerceValue(resp.NewState)
   326  		if err != nil {
   327  			resp.Diagnostics = resp.Diagnostics.Append(err)
   328  		}
   329  		resp.NewState = newState
   330  		return resp
   331  	}
   332  
   333  	// otherwise just return the same state we received
   334  	resp.NewState = r.PriorState
   335  	resp.Private = r.Private
   336  	return resp
   337  }
   338  
   339  func (p *MockProvider) PlanResourceChange(r providers.PlanResourceChangeRequest) (resp providers.PlanResourceChangeResponse) {
   340  	p.Lock()
   341  	defer p.Unlock()
   342  
   343  	if !p.ConfigureProviderCalled {
   344  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before PlanResourceChange %q", r.TypeName))
   345  		return resp
   346  	}
   347  
   348  	p.PlanResourceChangeCalled = true
   349  	p.PlanResourceChangeRequest = r
   350  
   351  	if p.PlanResourceChangeFn != nil {
   352  		return p.PlanResourceChangeFn(r)
   353  	}
   354  
   355  	if p.PlanResourceChangeResponse != nil {
   356  		return *p.PlanResourceChangeResponse
   357  	}
   358  
   359  	// this is a destroy plan,
   360  	if r.ProposedNewState.IsNull() {
   361  		resp.PlannedState = r.ProposedNewState
   362  		resp.PlannedPrivate = r.PriorPrivate
   363  		return resp
   364  	}
   365  
   366  	schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName]
   367  	if !ok {
   368  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName))
   369  		return resp
   370  	}
   371  
   372  	// The default plan behavior is to accept the proposed value, and mark all
   373  	// nil computed attributes as unknown.
   374  	val, err := cty.Transform(r.ProposedNewState, func(path cty.Path, v cty.Value) (cty.Value, error) {
   375  		// We're only concerned with known null values, which can be computed
   376  		// by the provider.
   377  		if !v.IsKnown() {
   378  			return v, nil
   379  		}
   380  
   381  		attrSchema := schema.Block.AttributeByPath(path)
   382  		if attrSchema == nil {
   383  			// this is an intermediate path which does not represent an attribute
   384  			return v, nil
   385  		}
   386  
   387  		// get the current configuration value, to detect when a
   388  		// computed+optional attributes has become unset
   389  		configVal, err := path.Apply(r.Config)
   390  		if err != nil {
   391  			return v, err
   392  		}
   393  
   394  		switch {
   395  		case attrSchema.Computed && !attrSchema.Optional && v.IsNull():
   396  			// this is the easy path, this value is not yet set, and _must_ be computed
   397  			return cty.UnknownVal(v.Type()), nil
   398  
   399  		case attrSchema.Computed && attrSchema.Optional && !v.IsNull() && configVal.IsNull():
   400  			// If an optional+computed value has gone from set to unset, it
   401  			// becomes computed. (this was not possible to do with legacy
   402  			// providers)
   403  			return cty.UnknownVal(v.Type()), nil
   404  		}
   405  
   406  		return v, nil
   407  	})
   408  	if err != nil {
   409  		resp.Diagnostics = resp.Diagnostics.Append(err)
   410  		return resp
   411  	}
   412  
   413  	resp.PlannedPrivate = r.PriorPrivate
   414  	resp.PlannedState = val
   415  
   416  	return resp
   417  }
   418  
   419  func (p *MockProvider) ApplyResourceChange(r providers.ApplyResourceChangeRequest) (resp providers.ApplyResourceChangeResponse) {
   420  	p.Lock()
   421  	p.ApplyResourceChangeCalled = true
   422  	p.ApplyResourceChangeRequest = r
   423  	p.Unlock()
   424  
   425  	if !p.ConfigureProviderCalled {
   426  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ApplyResourceChange %q", r.TypeName))
   427  		return resp
   428  	}
   429  
   430  	if p.ApplyResourceChangeFn != nil {
   431  		return p.ApplyResourceChangeFn(r)
   432  	}
   433  
   434  	if p.ApplyResourceChangeResponse != nil {
   435  		return *p.ApplyResourceChangeResponse
   436  	}
   437  
   438  	schema, ok := p.getProviderSchema().ResourceTypes[r.TypeName]
   439  	if !ok {
   440  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", r.TypeName))
   441  		return resp
   442  	}
   443  
   444  	// if the value is nil, we return that directly to correspond to a delete
   445  	if r.PlannedState.IsNull() {
   446  		resp.NewState = cty.NullVal(schema.Block.ImpliedType())
   447  		return resp
   448  	}
   449  
   450  	val, err := schema.Block.CoerceValue(r.PlannedState)
   451  	if err != nil {
   452  		resp.Diagnostics = resp.Diagnostics.Append(err)
   453  		return resp
   454  	}
   455  
   456  	// the default behavior will be to create the minimal valid apply value by
   457  	// setting unknowns (which correspond to computed attributes) to a zero
   458  	// value.
   459  	val, _ = cty.Transform(val, func(path cty.Path, v cty.Value) (cty.Value, error) {
   460  		if !v.IsKnown() {
   461  			ty := v.Type()
   462  			switch {
   463  			case ty == cty.String:
   464  				return cty.StringVal(""), nil
   465  			case ty == cty.Number:
   466  				return cty.NumberIntVal(0), nil
   467  			case ty == cty.Bool:
   468  				return cty.False, nil
   469  			case ty.IsMapType():
   470  				return cty.MapValEmpty(ty.ElementType()), nil
   471  			case ty.IsListType():
   472  				return cty.ListValEmpty(ty.ElementType()), nil
   473  			default:
   474  				return cty.NullVal(ty), nil
   475  			}
   476  		}
   477  		return v, nil
   478  	})
   479  
   480  	resp.NewState = val
   481  	resp.Private = r.PlannedPrivate
   482  
   483  	return resp
   484  }
   485  
   486  func (p *MockProvider) ImportResourceState(r providers.ImportResourceStateRequest) (resp providers.ImportResourceStateResponse) {
   487  	p.Lock()
   488  	defer p.Unlock()
   489  
   490  	if !p.ConfigureProviderCalled {
   491  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ImportResourceState %q", r.TypeName))
   492  		return resp
   493  	}
   494  
   495  	p.ImportResourceStateCalled = true
   496  	p.ImportResourceStateRequest = r
   497  	if p.ImportResourceStateFn != nil {
   498  		return p.ImportResourceStateFn(r)
   499  	}
   500  
   501  	if p.ImportResourceStateResponse != nil {
   502  		resp = *p.ImportResourceStateResponse
   503  		// fixup the cty value to match the schema
   504  		for i, res := range resp.ImportedResources {
   505  			schema, ok := p.getProviderSchema().ResourceTypes[res.TypeName]
   506  			if !ok {
   507  				resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("no schema found for %q", res.TypeName))
   508  				return resp
   509  			}
   510  
   511  			var err error
   512  			res.State, err = schema.Block.CoerceValue(res.State)
   513  			if err != nil {
   514  				resp.Diagnostics = resp.Diagnostics.Append(err)
   515  				return resp
   516  			}
   517  
   518  			resp.ImportedResources[i] = res
   519  		}
   520  	}
   521  
   522  	return resp
   523  }
   524  
   525  func (p *MockProvider) ReadDataSource(r providers.ReadDataSourceRequest) (resp providers.ReadDataSourceResponse) {
   526  	p.Lock()
   527  	defer p.Unlock()
   528  
   529  	if !p.ConfigureProviderCalled {
   530  		resp.Diagnostics = resp.Diagnostics.Append(fmt.Errorf("Configure not called before ReadDataSource %q", r.TypeName))
   531  		return resp
   532  	}
   533  
   534  	p.ReadDataSourceCalled = true
   535  	p.ReadDataSourceRequest = r
   536  
   537  	if p.ReadDataSourceFn != nil {
   538  		return p.ReadDataSourceFn(r)
   539  	}
   540  
   541  	if p.ReadDataSourceResponse != nil {
   542  		resp = *p.ReadDataSourceResponse
   543  	}
   544  
   545  	return resp
   546  }
   547  
   548  func (p *MockProvider) Close() error {
   549  	p.CloseCalled = true
   550  	return p.CloseError
   551  }