github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/grpcwrap/provider.go (about)

     1  package grpcwrap
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/eliastor/durgaform/internal/plugin/convert"
     7  	"github.com/eliastor/durgaform/internal/providers"
     8  	"github.com/eliastor/durgaform/internal/tfplugin5"
     9  	"github.com/zclconf/go-cty/cty"
    10  	ctyjson "github.com/zclconf/go-cty/cty/json"
    11  	"github.com/zclconf/go-cty/cty/msgpack"
    12  )
    13  
    14  // New wraps a providers.Interface to implement a grpc ProviderServer.
    15  // This is useful for creating a test binary out of an internal provider
    16  // implementation.
    17  func Provider(p providers.Interface) tfplugin5.ProviderServer {
    18  	return &provider{
    19  		provider: p,
    20  		schema:   p.GetProviderSchema(),
    21  	}
    22  }
    23  
    24  type provider struct {
    25  	provider providers.Interface
    26  	schema   providers.GetProviderSchemaResponse
    27  }
    28  
    29  func (p *provider) GetSchema(_ context.Context, req *tfplugin5.GetProviderSchema_Request) (*tfplugin5.GetProviderSchema_Response, error) {
    30  	resp := &tfplugin5.GetProviderSchema_Response{
    31  		ResourceSchemas:   make(map[string]*tfplugin5.Schema),
    32  		DataSourceSchemas: make(map[string]*tfplugin5.Schema),
    33  	}
    34  
    35  	resp.Provider = &tfplugin5.Schema{
    36  		Block: &tfplugin5.Schema_Block{},
    37  	}
    38  	if p.schema.Provider.Block != nil {
    39  		resp.Provider.Block = convert.ConfigSchemaToProto(p.schema.Provider.Block)
    40  	}
    41  
    42  	resp.ProviderMeta = &tfplugin5.Schema{
    43  		Block: &tfplugin5.Schema_Block{},
    44  	}
    45  	if p.schema.ProviderMeta.Block != nil {
    46  		resp.ProviderMeta.Block = convert.ConfigSchemaToProto(p.schema.ProviderMeta.Block)
    47  	}
    48  
    49  	for typ, res := range p.schema.ResourceTypes {
    50  		resp.ResourceSchemas[typ] = &tfplugin5.Schema{
    51  			Version: res.Version,
    52  			Block:   convert.ConfigSchemaToProto(res.Block),
    53  		}
    54  	}
    55  	for typ, dat := range p.schema.DataSources {
    56  		resp.DataSourceSchemas[typ] = &tfplugin5.Schema{
    57  			Version: dat.Version,
    58  			Block:   convert.ConfigSchemaToProto(dat.Block),
    59  		}
    60  	}
    61  
    62  	resp.ServerCapabilities = &tfplugin5.GetProviderSchema_ServerCapabilities{
    63  		PlanDestroy: p.schema.ServerCapabilities.PlanDestroy,
    64  	}
    65  
    66  	// include any diagnostics from the original GetSchema call
    67  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, p.schema.Diagnostics)
    68  
    69  	return resp, nil
    70  }
    71  
    72  func (p *provider) PrepareProviderConfig(_ context.Context, req *tfplugin5.PrepareProviderConfig_Request) (*tfplugin5.PrepareProviderConfig_Response, error) {
    73  	resp := &tfplugin5.PrepareProviderConfig_Response{}
    74  	ty := p.schema.Provider.Block.ImpliedType()
    75  
    76  	configVal, err := decodeDynamicValue(req.Config, ty)
    77  	if err != nil {
    78  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
    79  		return resp, nil
    80  	}
    81  
    82  	prepareResp := p.provider.ValidateProviderConfig(providers.ValidateProviderConfigRequest{
    83  		Config: configVal,
    84  	})
    85  
    86  	// the PreparedConfig value is no longer used
    87  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, prepareResp.Diagnostics)
    88  	return resp, nil
    89  }
    90  
    91  func (p *provider) ValidateResourceTypeConfig(_ context.Context, req *tfplugin5.ValidateResourceTypeConfig_Request) (*tfplugin5.ValidateResourceTypeConfig_Response, error) {
    92  	resp := &tfplugin5.ValidateResourceTypeConfig_Response{}
    93  	ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType()
    94  
    95  	configVal, err := decodeDynamicValue(req.Config, ty)
    96  	if err != nil {
    97  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
    98  		return resp, nil
    99  	}
   100  
   101  	validateResp := p.provider.ValidateResourceConfig(providers.ValidateResourceConfigRequest{
   102  		TypeName: req.TypeName,
   103  		Config:   configVal,
   104  	})
   105  
   106  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics)
   107  	return resp, nil
   108  }
   109  
   110  func (p *provider) ValidateDataSourceConfig(_ context.Context, req *tfplugin5.ValidateDataSourceConfig_Request) (*tfplugin5.ValidateDataSourceConfig_Response, error) {
   111  	resp := &tfplugin5.ValidateDataSourceConfig_Response{}
   112  	ty := p.schema.DataSources[req.TypeName].Block.ImpliedType()
   113  
   114  	configVal, err := decodeDynamicValue(req.Config, ty)
   115  	if err != nil {
   116  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   117  		return resp, nil
   118  	}
   119  
   120  	validateResp := p.provider.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{
   121  		TypeName: req.TypeName,
   122  		Config:   configVal,
   123  	})
   124  
   125  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, validateResp.Diagnostics)
   126  	return resp, nil
   127  }
   128  
   129  func (p *provider) UpgradeResourceState(_ context.Context, req *tfplugin5.UpgradeResourceState_Request) (*tfplugin5.UpgradeResourceState_Response, error) {
   130  	resp := &tfplugin5.UpgradeResourceState_Response{}
   131  	ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType()
   132  
   133  	upgradeResp := p.provider.UpgradeResourceState(providers.UpgradeResourceStateRequest{
   134  		TypeName:     req.TypeName,
   135  		Version:      req.Version,
   136  		RawStateJSON: req.RawState.Json,
   137  	})
   138  
   139  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, upgradeResp.Diagnostics)
   140  	if upgradeResp.Diagnostics.HasErrors() {
   141  		return resp, nil
   142  	}
   143  
   144  	dv, err := encodeDynamicValue(upgradeResp.UpgradedState, ty)
   145  	if err != nil {
   146  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   147  		return resp, nil
   148  	}
   149  
   150  	resp.UpgradedState = dv
   151  
   152  	return resp, nil
   153  }
   154  
   155  func (p *provider) Configure(_ context.Context, req *tfplugin5.Configure_Request) (*tfplugin5.Configure_Response, error) {
   156  	resp := &tfplugin5.Configure_Response{}
   157  	ty := p.schema.Provider.Block.ImpliedType()
   158  
   159  	configVal, err := decodeDynamicValue(req.Config, ty)
   160  	if err != nil {
   161  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   162  		return resp, nil
   163  	}
   164  
   165  	configureResp := p.provider.ConfigureProvider(providers.ConfigureProviderRequest{
   166  		DurgaformVersion: req.TerraformVersion,
   167  		Config:           configVal,
   168  	})
   169  
   170  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, configureResp.Diagnostics)
   171  	return resp, nil
   172  }
   173  
   174  func (p *provider) ReadResource(_ context.Context, req *tfplugin5.ReadResource_Request) (*tfplugin5.ReadResource_Response, error) {
   175  	resp := &tfplugin5.ReadResource_Response{}
   176  	ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType()
   177  
   178  	stateVal, err := decodeDynamicValue(req.CurrentState, ty)
   179  	if err != nil {
   180  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   181  		return resp, nil
   182  	}
   183  
   184  	metaTy := p.schema.ProviderMeta.Block.ImpliedType()
   185  	metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy)
   186  	if err != nil {
   187  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   188  		return resp, nil
   189  	}
   190  
   191  	readResp := p.provider.ReadResource(providers.ReadResourceRequest{
   192  		TypeName:     req.TypeName,
   193  		PriorState:   stateVal,
   194  		Private:      req.Private,
   195  		ProviderMeta: metaVal,
   196  	})
   197  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics)
   198  	if readResp.Diagnostics.HasErrors() {
   199  		return resp, nil
   200  	}
   201  	resp.Private = readResp.Private
   202  
   203  	dv, err := encodeDynamicValue(readResp.NewState, ty)
   204  	if err != nil {
   205  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   206  		return resp, nil
   207  	}
   208  	resp.NewState = dv
   209  
   210  	return resp, nil
   211  }
   212  
   213  func (p *provider) PlanResourceChange(_ context.Context, req *tfplugin5.PlanResourceChange_Request) (*tfplugin5.PlanResourceChange_Response, error) {
   214  	resp := &tfplugin5.PlanResourceChange_Response{}
   215  	ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType()
   216  
   217  	priorStateVal, err := decodeDynamicValue(req.PriorState, ty)
   218  	if err != nil {
   219  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   220  		return resp, nil
   221  	}
   222  
   223  	proposedStateVal, err := decodeDynamicValue(req.ProposedNewState, ty)
   224  	if err != nil {
   225  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   226  		return resp, nil
   227  	}
   228  
   229  	configVal, err := decodeDynamicValue(req.Config, ty)
   230  	if err != nil {
   231  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   232  		return resp, nil
   233  	}
   234  
   235  	metaTy := p.schema.ProviderMeta.Block.ImpliedType()
   236  	metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy)
   237  	if err != nil {
   238  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   239  		return resp, nil
   240  	}
   241  
   242  	planResp := p.provider.PlanResourceChange(providers.PlanResourceChangeRequest{
   243  		TypeName:         req.TypeName,
   244  		PriorState:       priorStateVal,
   245  		ProposedNewState: proposedStateVal,
   246  		Config:           configVal,
   247  		PriorPrivate:     req.PriorPrivate,
   248  		ProviderMeta:     metaVal,
   249  	})
   250  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, planResp.Diagnostics)
   251  	if planResp.Diagnostics.HasErrors() {
   252  		return resp, nil
   253  	}
   254  
   255  	resp.PlannedPrivate = planResp.PlannedPrivate
   256  
   257  	resp.PlannedState, err = encodeDynamicValue(planResp.PlannedState, ty)
   258  	if err != nil {
   259  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   260  		return resp, nil
   261  	}
   262  
   263  	for _, path := range planResp.RequiresReplace {
   264  		resp.RequiresReplace = append(resp.RequiresReplace, convert.PathToAttributePath(path))
   265  	}
   266  
   267  	return resp, nil
   268  }
   269  
   270  func (p *provider) ApplyResourceChange(_ context.Context, req *tfplugin5.ApplyResourceChange_Request) (*tfplugin5.ApplyResourceChange_Response, error) {
   271  	resp := &tfplugin5.ApplyResourceChange_Response{}
   272  	ty := p.schema.ResourceTypes[req.TypeName].Block.ImpliedType()
   273  
   274  	priorStateVal, err := decodeDynamicValue(req.PriorState, ty)
   275  	if err != nil {
   276  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   277  		return resp, nil
   278  	}
   279  
   280  	plannedStateVal, err := decodeDynamicValue(req.PlannedState, ty)
   281  	if err != nil {
   282  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   283  		return resp, nil
   284  	}
   285  
   286  	configVal, err := decodeDynamicValue(req.Config, ty)
   287  	if err != nil {
   288  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   289  		return resp, nil
   290  	}
   291  
   292  	metaTy := p.schema.ProviderMeta.Block.ImpliedType()
   293  	metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy)
   294  	if err != nil {
   295  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   296  		return resp, nil
   297  	}
   298  
   299  	applyResp := p.provider.ApplyResourceChange(providers.ApplyResourceChangeRequest{
   300  		TypeName:       req.TypeName,
   301  		PriorState:     priorStateVal,
   302  		PlannedState:   plannedStateVal,
   303  		Config:         configVal,
   304  		PlannedPrivate: req.PlannedPrivate,
   305  		ProviderMeta:   metaVal,
   306  	})
   307  
   308  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, applyResp.Diagnostics)
   309  	if applyResp.Diagnostics.HasErrors() {
   310  		return resp, nil
   311  	}
   312  	resp.Private = applyResp.Private
   313  
   314  	resp.NewState, err = encodeDynamicValue(applyResp.NewState, ty)
   315  	if err != nil {
   316  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   317  		return resp, nil
   318  	}
   319  
   320  	return resp, nil
   321  }
   322  
   323  func (p *provider) ImportResourceState(_ context.Context, req *tfplugin5.ImportResourceState_Request) (*tfplugin5.ImportResourceState_Response, error) {
   324  	resp := &tfplugin5.ImportResourceState_Response{}
   325  
   326  	importResp := p.provider.ImportResourceState(providers.ImportResourceStateRequest{
   327  		TypeName: req.TypeName,
   328  		ID:       req.Id,
   329  	})
   330  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, importResp.Diagnostics)
   331  
   332  	for _, res := range importResp.ImportedResources {
   333  		ty := p.schema.ResourceTypes[res.TypeName].Block.ImpliedType()
   334  		state, err := encodeDynamicValue(res.State, ty)
   335  		if err != nil {
   336  			resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   337  			continue
   338  		}
   339  
   340  		resp.ImportedResources = append(resp.ImportedResources, &tfplugin5.ImportResourceState_ImportedResource{
   341  			TypeName: res.TypeName,
   342  			State:    state,
   343  			Private:  res.Private,
   344  		})
   345  	}
   346  
   347  	return resp, nil
   348  }
   349  
   350  func (p *provider) ReadDataSource(_ context.Context, req *tfplugin5.ReadDataSource_Request) (*tfplugin5.ReadDataSource_Response, error) {
   351  	resp := &tfplugin5.ReadDataSource_Response{}
   352  	ty := p.schema.DataSources[req.TypeName].Block.ImpliedType()
   353  
   354  	configVal, err := decodeDynamicValue(req.Config, ty)
   355  	if err != nil {
   356  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   357  		return resp, nil
   358  	}
   359  
   360  	metaTy := p.schema.ProviderMeta.Block.ImpliedType()
   361  	metaVal, err := decodeDynamicValue(req.ProviderMeta, metaTy)
   362  	if err != nil {
   363  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   364  		return resp, nil
   365  	}
   366  
   367  	readResp := p.provider.ReadDataSource(providers.ReadDataSourceRequest{
   368  		TypeName:     req.TypeName,
   369  		Config:       configVal,
   370  		ProviderMeta: metaVal,
   371  	})
   372  	resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, readResp.Diagnostics)
   373  	if readResp.Diagnostics.HasErrors() {
   374  		return resp, nil
   375  	}
   376  
   377  	resp.State, err = encodeDynamicValue(readResp.State, ty)
   378  	if err != nil {
   379  		resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
   380  		return resp, nil
   381  	}
   382  
   383  	return resp, nil
   384  }
   385  
   386  func (p *provider) Stop(context.Context, *tfplugin5.Stop_Request) (*tfplugin5.Stop_Response, error) {
   387  	resp := &tfplugin5.Stop_Response{}
   388  	err := p.provider.Stop()
   389  	if err != nil {
   390  		resp.Error = err.Error()
   391  	}
   392  	return resp, nil
   393  }
   394  
   395  // decode a DynamicValue from either the JSON or MsgPack encoding.
   396  func decodeDynamicValue(v *tfplugin5.DynamicValue, ty cty.Type) (cty.Value, error) {
   397  	// always return a valid value
   398  	var err error
   399  	res := cty.NullVal(ty)
   400  	if v == nil {
   401  		return res, nil
   402  	}
   403  
   404  	switch {
   405  	case len(v.Msgpack) > 0:
   406  		res, err = msgpack.Unmarshal(v.Msgpack, ty)
   407  	case len(v.Json) > 0:
   408  		res, err = ctyjson.Unmarshal(v.Json, ty)
   409  	}
   410  	return res, err
   411  }
   412  
   413  // encode a cty.Value into a DynamicValue msgpack payload.
   414  func encodeDynamicValue(v cty.Value, ty cty.Type) (*tfplugin5.DynamicValue, error) {
   415  	mp, err := msgpack.Marshal(v, ty)
   416  	return &tfplugin5.DynamicValue{
   417  		Msgpack: mp,
   418  	}, err
   419  }