github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/terraform/context_plugins.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"sync"
     7  
     8  	"github.com/muratcelep/terraform/not-internal/addrs"
     9  	"github.com/muratcelep/terraform/not-internal/configs/configschema"
    10  	"github.com/muratcelep/terraform/not-internal/providers"
    11  	"github.com/muratcelep/terraform/not-internal/provisioners"
    12  )
    13  
    14  // contextPlugins represents a library of available plugins (providers and
    15  // provisioners) which we assume will all be used with the same
    16  // terraform.Context, and thus it'll be safe to cache certain information
    17  // about the providers for performance reasons.
    18  type contextPlugins struct {
    19  	providerFactories    map[addrs.Provider]providers.Factory
    20  	provisionerFactories map[string]provisioners.Factory
    21  
    22  	// We memoize the schemas we've previously loaded in here, to avoid
    23  	// repeatedly paying the cost of activating the same plugins to access
    24  	// their schemas in various different spots. We use schemas for many
    25  	// purposes in Terraform, so there isn't a single choke point where
    26  	// it makes sense to preload all of them.
    27  	providerSchemas    map[addrs.Provider]*ProviderSchema
    28  	provisionerSchemas map[string]*configschema.Block
    29  	schemasLock        sync.Mutex
    30  }
    31  
    32  func newContextPlugins(providerFactories map[addrs.Provider]providers.Factory, provisionerFactories map[string]provisioners.Factory) *contextPlugins {
    33  	ret := &contextPlugins{
    34  		providerFactories:    providerFactories,
    35  		provisionerFactories: provisionerFactories,
    36  	}
    37  	ret.init()
    38  	return ret
    39  }
    40  
    41  func (cp *contextPlugins) init() {
    42  	cp.providerSchemas = make(map[addrs.Provider]*ProviderSchema, len(cp.providerFactories))
    43  	cp.provisionerSchemas = make(map[string]*configschema.Block, len(cp.provisionerFactories))
    44  }
    45  
    46  func (cp *contextPlugins) HasProvider(addr addrs.Provider) bool {
    47  	_, ok := cp.providerFactories[addr]
    48  	return ok
    49  }
    50  
    51  func (cp *contextPlugins) NewProviderInstance(addr addrs.Provider) (providers.Interface, error) {
    52  	f, ok := cp.providerFactories[addr]
    53  	if !ok {
    54  		return nil, fmt.Errorf("unavailable provider %q", addr.String())
    55  	}
    56  
    57  	return f()
    58  
    59  }
    60  
    61  func (cp *contextPlugins) HasProvisioner(typ string) bool {
    62  	_, ok := cp.provisionerFactories[typ]
    63  	return ok
    64  }
    65  
    66  func (cp *contextPlugins) NewProvisionerInstance(typ string) (provisioners.Interface, error) {
    67  	f, ok := cp.provisionerFactories[typ]
    68  	if !ok {
    69  		return nil, fmt.Errorf("unavailable provisioner %q", typ)
    70  	}
    71  
    72  	return f()
    73  }
    74  
    75  // ProviderSchema uses a temporary instance of the provider with the given
    76  // address to obtain the full schema for all aspects of that provider.
    77  //
    78  // ProviderSchema memoizes results by unique provider address, so it's fine
    79  // to repeatedly call this method with the same address if various different
    80  // parts of Terraform all need the same schema information.
    81  func (cp *contextPlugins) ProviderSchema(addr addrs.Provider) (*ProviderSchema, error) {
    82  	cp.schemasLock.Lock()
    83  	defer cp.schemasLock.Unlock()
    84  
    85  	if schema, ok := cp.providerSchemas[addr]; ok {
    86  		return schema, nil
    87  	}
    88  
    89  	log.Printf("[TRACE] terraform.contextPlugins: Initializing provider %q to read its schema", addr)
    90  
    91  	provider, err := cp.NewProviderInstance(addr)
    92  	if err != nil {
    93  		return nil, fmt.Errorf("failed to instantiate provider %q to obtain schema: %s", addr, err)
    94  	}
    95  	defer provider.Close()
    96  
    97  	resp := provider.GetProviderSchema()
    98  	if resp.Diagnostics.HasErrors() {
    99  		return nil, fmt.Errorf("failed to retrieve schema from provider %q: %s", addr, resp.Diagnostics.Err())
   100  	}
   101  
   102  	s := &ProviderSchema{
   103  		Provider:      resp.Provider.Block,
   104  		ResourceTypes: make(map[string]*configschema.Block),
   105  		DataSources:   make(map[string]*configschema.Block),
   106  
   107  		ResourceTypeSchemaVersions: make(map[string]uint64),
   108  	}
   109  
   110  	if resp.Provider.Version < 0 {
   111  		// We're not using the version numbers here yet, but we'll check
   112  		// for validity anyway in case we start using them in future.
   113  		return nil, fmt.Errorf("provider %s has invalid negative schema version for its configuration blocks,which is a bug in the provider ", addr)
   114  	}
   115  
   116  	for t, r := range resp.ResourceTypes {
   117  		if err := r.Block.InternalValidate(); err != nil {
   118  			return nil, fmt.Errorf("provider %s has invalid schema for managed resource type %q, which is a bug in the provider: %q", addr, t, err)
   119  		}
   120  		s.ResourceTypes[t] = r.Block
   121  		s.ResourceTypeSchemaVersions[t] = uint64(r.Version)
   122  		if r.Version < 0 {
   123  			return nil, fmt.Errorf("provider %s has invalid negative schema version for managed resource type %q, which is a bug in the provider", addr, t)
   124  		}
   125  	}
   126  
   127  	for t, d := range resp.DataSources {
   128  		if err := d.Block.InternalValidate(); err != nil {
   129  			return nil, fmt.Errorf("provider %s has invalid schema for data resource type %q, which is a bug in the provider: %q", addr, t, err)
   130  		}
   131  		s.DataSources[t] = d.Block
   132  		if d.Version < 0 {
   133  			// We're not using the version numbers here yet, but we'll check
   134  			// for validity anyway in case we start using them in future.
   135  			return nil, fmt.Errorf("provider %s has invalid negative schema version for data resource type %q, which is a bug in the provider", addr, t)
   136  		}
   137  	}
   138  
   139  	if resp.ProviderMeta.Block != nil {
   140  		s.ProviderMeta = resp.ProviderMeta.Block
   141  	}
   142  
   143  	cp.providerSchemas[addr] = s
   144  	return s, nil
   145  }
   146  
   147  // ProviderConfigSchema is a helper wrapper around ProviderSchema which first
   148  // reads the full schema of the given provider and then extracts just the
   149  // provider's configuration schema, which defines what's expected in a
   150  // "provider" block in the configuration when configuring this provider.
   151  func (cp *contextPlugins) ProviderConfigSchema(providerAddr addrs.Provider) (*configschema.Block, error) {
   152  	providerSchema, err := cp.ProviderSchema(providerAddr)
   153  	if err != nil {
   154  		return nil, err
   155  	}
   156  
   157  	return providerSchema.Provider, nil
   158  }
   159  
   160  // ResourceTypeSchema is a helper wrapper around ProviderSchema which first
   161  // reads the schema of the given provider and then tries to find the schema
   162  // for the resource type of the given resource mode in that provider.
   163  //
   164  // ResourceTypeSchema will return an error if the provider schema lookup
   165  // fails, but will return nil if the provider schema lookup succeeds but then
   166  // the provider doesn't have a resource of the requested type.
   167  //
   168  // Managed resource types have versioned schemas, so the second return value
   169  // is the current schema version number for the requested resource. The version
   170  // is irrelevant for other resource modes.
   171  func (cp *contextPlugins) ResourceTypeSchema(providerAddr addrs.Provider, resourceMode addrs.ResourceMode, resourceType string) (*configschema.Block, uint64, error) {
   172  	providerSchema, err := cp.ProviderSchema(providerAddr)
   173  	if err != nil {
   174  		return nil, 0, err
   175  	}
   176  
   177  	schema, version := providerSchema.SchemaForResourceType(resourceMode, resourceType)
   178  	return schema, version, nil
   179  }
   180  
   181  // ProvisionerSchema uses a temporary instance of the provisioner with the
   182  // given type name to obtain the schema for that provisioner's configuration.
   183  //
   184  // ProvisionerSchema memoizes results by provisioner type name, so it's fine
   185  // to repeatedly call this method with the same name if various different
   186  // parts of Terraform all need the same schema information.
   187  func (cp *contextPlugins) ProvisionerSchema(typ string) (*configschema.Block, error) {
   188  	cp.schemasLock.Lock()
   189  	defer cp.schemasLock.Unlock()
   190  
   191  	if schema, ok := cp.provisionerSchemas[typ]; ok {
   192  		return schema, nil
   193  	}
   194  
   195  	log.Printf("[TRACE] terraform.contextPlugins: Initializing provisioner %q to read its schema", typ)
   196  	provisioner, err := cp.NewProvisionerInstance(typ)
   197  	if err != nil {
   198  		return nil, fmt.Errorf("failed to instantiate provisioner %q to obtain schema: %s", typ, err)
   199  	}
   200  	defer provisioner.Close()
   201  
   202  	resp := provisioner.GetSchema()
   203  	if resp.Diagnostics.HasErrors() {
   204  		return nil, fmt.Errorf("failed to retrieve schema from provisioner %q: %s", typ, resp.Diagnostics.Err())
   205  	}
   206  
   207  	cp.provisionerSchemas[typ] = resp.Provisioner
   208  	return resp.Provisioner, nil
   209  }