]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - vendor/github.com/hashicorp/terraform/terraform/schemas.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / terraform / schemas.go
index ec46efcf7dd64577e6df36a3cd7d67aa5edaf4fb..62991c82d091657c56ef613010306020b18ce226 100644 (file)
 package terraform
 
 import (
-       "github.com/hashicorp/terraform/config/configschema"
+       "fmt"
+       "log"
+
+       "github.com/hashicorp/terraform/addrs"
+       "github.com/hashicorp/terraform/configs"
+       "github.com/hashicorp/terraform/configs/configschema"
+       "github.com/hashicorp/terraform/providers"
+       "github.com/hashicorp/terraform/states"
+       "github.com/hashicorp/terraform/tfdiags"
 )
 
+// Schemas is a container for various kinds of schema that Terraform needs
+// during processing.
 type Schemas struct {
-       Providers ProviderSchemas
+       Providers    map[string]*ProviderSchema
+       Provisioners map[string]*configschema.Block
+}
+
+// ProviderSchema returns the entire ProviderSchema object that was produced
+// by the plugin for the given provider, or nil if no such schema is available.
+//
+// It's usually better to go use the more precise methods offered by type
+// Schemas to handle this detail automatically.
+func (ss *Schemas) ProviderSchema(typeName string) *ProviderSchema {
+       if ss.Providers == nil {
+               return nil
+       }
+       return ss.Providers[typeName]
+}
+
+// ProviderConfig returns the schema for the provider configuration of the
+// given provider type, or nil if no such schema is available.
+func (ss *Schemas) ProviderConfig(typeName string) *configschema.Block {
+       ps := ss.ProviderSchema(typeName)
+       if ps == nil {
+               return nil
+       }
+       return ps.Provider
+}
+
+// ResourceTypeConfig returns the schema for the configuration of a given
+// resource type belonging to a given provider type, or nil of no such
+// schema is available.
+//
+// In many cases the provider type is inferrable from the resource type name,
+// but this is not always true because users can override the provider for
+// a resource using the "provider" meta-argument. Therefore it's important to
+// always pass the correct provider name, even though it many cases it feels
+// redundant.
+func (ss *Schemas) ResourceTypeConfig(providerType string, resourceMode addrs.ResourceMode, resourceType string) (block *configschema.Block, schemaVersion uint64) {
+       ps := ss.ProviderSchema(providerType)
+       if ps == nil || ps.ResourceTypes == nil {
+               return nil, 0
+       }
+       return ps.SchemaForResourceType(resourceMode, resourceType)
+}
+
+// ProvisionerConfig returns the schema for the configuration of a given
+// provisioner, or nil of no such schema is available.
+func (ss *Schemas) ProvisionerConfig(name string) *configschema.Block {
+       return ss.Provisioners[name]
 }
 
-// ProviderSchemas is a map from provider names to provider schemas.
+// LoadSchemas searches the given configuration, state  and plan (any of which
+// may be nil) for constructs that have an associated schema, requests the
+// necessary schemas from the given component factory (which must _not_ be nil),
+// and returns a single object representing all of the necessary schemas.
 //
-// The names in this map are the direct plugin name (e.g. "aws") rather than
-// any alias name (e.g. "aws.foo"), since.
-type ProviderSchemas map[string]*ProviderSchema
+// If an error is returned, it may be a wrapped tfdiags.Diagnostics describing
+// errors across multiple separate objects. Errors here will usually indicate
+// either misbehavior on the part of one of the providers or of the provider
+// protocol itself. When returned with errors, the returned schemas object is
+// still valid but may be incomplete.
+func LoadSchemas(config *configs.Config, state *states.State, components contextComponentFactory) (*Schemas, error) {
+       schemas := &Schemas{
+               Providers:    map[string]*ProviderSchema{},
+               Provisioners: map[string]*configschema.Block{},
+       }
+       var diags tfdiags.Diagnostics
+
+       newDiags := loadProviderSchemas(schemas.Providers, config, state, components)
+       diags = diags.Append(newDiags)
+       newDiags = loadProvisionerSchemas(schemas.Provisioners, config, components)
+       diags = diags.Append(newDiags)
+
+       return schemas, diags.Err()
+}
+
+func loadProviderSchemas(schemas map[string]*ProviderSchema, config *configs.Config, state *states.State, components contextComponentFactory) tfdiags.Diagnostics {
+       var diags tfdiags.Diagnostics
+
+       ensure := func(typeName string) {
+               if _, exists := schemas[typeName]; exists {
+                       return
+               }
+
+               log.Printf("[TRACE] LoadSchemas: retrieving schema for provider type %q", typeName)
+               provider, err := components.ResourceProvider(typeName, "early/"+typeName)
+               if err != nil {
+                       // We'll put a stub in the map so we won't re-attempt this on
+                       // future calls.
+                       schemas[typeName] = &ProviderSchema{}
+                       diags = diags.Append(
+                               fmt.Errorf("Failed to instantiate provider %q to obtain schema: %s", typeName, err),
+                       )
+                       return
+               }
+               defer func() {
+                       provider.Close()
+               }()
+
+               resp := provider.GetSchema()
+               if resp.Diagnostics.HasErrors() {
+                       // We'll put a stub in the map so we won't re-attempt this on
+                       // future calls.
+                       schemas[typeName] = &ProviderSchema{}
+                       diags = diags.Append(
+                               fmt.Errorf("Failed to retrieve schema from provider %q: %s", typeName, resp.Diagnostics.Err()),
+                       )
+                       return
+               }
+
+               s := &ProviderSchema{
+                       Provider:      resp.Provider.Block,
+                       ResourceTypes: make(map[string]*configschema.Block),
+                       DataSources:   make(map[string]*configschema.Block),
+
+                       ResourceTypeSchemaVersions: make(map[string]uint64),
+               }
+
+               if resp.Provider.Version < 0 {
+                       // We're not using the version numbers here yet, but we'll check
+                       // for validity anyway in case we start using them in future.
+                       diags = diags.Append(
+                               fmt.Errorf("invalid negative schema version provider configuration for provider %q", typeName),
+                       )
+               }
+
+               for t, r := range resp.ResourceTypes {
+                       s.ResourceTypes[t] = r.Block
+                       s.ResourceTypeSchemaVersions[t] = uint64(r.Version)
+                       if r.Version < 0 {
+                               diags = diags.Append(
+                                       fmt.Errorf("invalid negative schema version for resource type %s in provider %q", t, typeName),
+                               )
+                       }
+               }
+
+               for t, d := range resp.DataSources {
+                       s.DataSources[t] = d.Block
+                       if d.Version < 0 {
+                               // We're not using the version numbers here yet, but we'll check
+                               // for validity anyway in case we start using them in future.
+                               diags = diags.Append(
+                                       fmt.Errorf("invalid negative schema version for data source %s in provider %q", t, typeName),
+                               )
+                       }
+               }
+
+               schemas[typeName] = s
+       }
+
+       if config != nil {
+               for _, typeName := range config.ProviderTypes() {
+                       ensure(typeName)
+               }
+       }
+
+       if state != nil {
+               needed := providers.AddressedTypesAbs(state.ProviderAddrs())
+               for _, typeName := range needed {
+                       ensure(typeName)
+               }
+       }
+
+       return diags
+}
+
+func loadProvisionerSchemas(schemas map[string]*configschema.Block, config *configs.Config, components contextComponentFactory) tfdiags.Diagnostics {
+       var diags tfdiags.Diagnostics
+
+       ensure := func(name string) {
+               if _, exists := schemas[name]; exists {
+                       return
+               }
+
+               log.Printf("[TRACE] LoadSchemas: retrieving schema for provisioner %q", name)
+               provisioner, err := components.ResourceProvisioner(name, "early/"+name)
+               if err != nil {
+                       // We'll put a stub in the map so we won't re-attempt this on
+                       // future calls.
+                       schemas[name] = &configschema.Block{}
+                       diags = diags.Append(
+                               fmt.Errorf("Failed to instantiate provisioner %q to obtain schema: %s", name, err),
+                       )
+                       return
+               }
+               defer func() {
+                       if closer, ok := provisioner.(ResourceProvisionerCloser); ok {
+                               closer.Close()
+                       }
+               }()
+
+               resp := provisioner.GetSchema()
+               if resp.Diagnostics.HasErrors() {
+                       // We'll put a stub in the map so we won't re-attempt this on
+                       // future calls.
+                       schemas[name] = &configschema.Block{}
+                       diags = diags.Append(
+                               fmt.Errorf("Failed to retrieve schema from provisioner %q: %s", name, resp.Diagnostics.Err()),
+                       )
+                       return
+               }
+
+               schemas[name] = resp.Provisioner
+       }
+
+       if config != nil {
+               for _, rc := range config.Module.ManagedResources {
+                       for _, pc := range rc.Managed.Provisioners {
+                               ensure(pc.Type)
+                       }
+               }
+
+               // Must also visit our child modules, recursively.
+               for _, cc := range config.Children {
+                       childDiags := loadProvisionerSchemas(schemas, cc, components)
+                       diags = diags.Append(childDiags)
+               }
+       }
+
+       return diags
+}
 
 // ProviderSchema represents the schema for a provider's own configuration
 // and the configuration for some or all of its resources and data sources.
@@ -24,6 +245,29 @@ type ProviderSchema struct {
        Provider      *configschema.Block
        ResourceTypes map[string]*configschema.Block
        DataSources   map[string]*configschema.Block
+
+       ResourceTypeSchemaVersions map[string]uint64
+}
+
+// SchemaForResourceType attempts to find a schema for the given mode and type.
+// Returns nil if no such schema is available.
+func (ps *ProviderSchema) SchemaForResourceType(mode addrs.ResourceMode, typeName string) (schema *configschema.Block, version uint64) {
+       switch mode {
+       case addrs.ManagedResourceMode:
+               return ps.ResourceTypes[typeName], ps.ResourceTypeSchemaVersions[typeName]
+       case addrs.DataResourceMode:
+               // Data resources don't have schema versions right now, since state is discarded for each refresh
+               return ps.DataSources[typeName], 0
+       default:
+               // Shouldn't happen, because the above cases are comprehensive.
+               return nil, 0
+       }
+}
+
+// SchemaForResourceAddr attempts to find a schema for the mode and type from
+// the given resource address. Returns nil if no such schema is available.
+func (ps *ProviderSchema) SchemaForResourceAddr(addr addrs.Resource) (schema *configschema.Block, version uint64) {
+       return ps.SchemaForResourceType(addr.Mode, addr.Type)
 }
 
 // ProviderSchemaRequest is used to describe to a ResourceProvider which