github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/addrs/provider_config.go (about)

     1  package addrs
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/terraform/tfdiags"
     7  
     8  	"github.com/hashicorp/hcl/v2"
     9  	"github.com/hashicorp/hcl/v2/hclsyntax"
    10  )
    11  
    12  // ProviderConfig is the address of a provider configuration.
    13  type ProviderConfig struct {
    14  	Type Provider
    15  
    16  	// If not empty, Alias identifies which non-default (aliased) provider
    17  	// configuration this address refers to.
    18  	Alias string
    19  }
    20  
    21  // NewDefaultProviderConfig returns the address of the default (un-aliased)
    22  // configuration for the provider with the given type name.
    23  func NewDefaultProviderConfig(typeName string) ProviderConfig {
    24  	return ProviderConfig{
    25  		Type: NewLegacyProvider(typeName),
    26  	}
    27  }
    28  
    29  // Absolute returns an AbsProviderConfig from the receiver and the given module
    30  // instance address.
    31  func (pc ProviderConfig) Absolute(module ModuleInstance) AbsProviderConfig {
    32  	return AbsProviderConfig{
    33  		Module:         module,
    34  		ProviderConfig: pc,
    35  	}
    36  }
    37  
    38  func (pc ProviderConfig) String() string {
    39  	if pc.Type.LegacyString() == "" {
    40  		// Should never happen; always indicates a bug
    41  		return "provider.<invalid>"
    42  	}
    43  
    44  	if pc.Alias != "" {
    45  		return fmt.Sprintf("provider.%s.%s", pc.Type.LegacyString(), pc.Alias)
    46  	}
    47  
    48  	return "provider." + pc.Type.LegacyString()
    49  }
    50  
    51  // StringCompact is an alternative to String that returns the form that can
    52  // be parsed by ParseProviderConfigCompact, without the "provider." prefix.
    53  func (pc ProviderConfig) StringCompact() string {
    54  	if pc.Alias != "" {
    55  		return fmt.Sprintf("%s.%s", pc.Type.LegacyString(), pc.Alias)
    56  	}
    57  	return pc.Type.LegacyString()
    58  }
    59  
    60  // AbsProviderConfig is the absolute address of a provider configuration
    61  // within a particular module instance.
    62  type AbsProviderConfig struct {
    63  	Module         ModuleInstance
    64  	ProviderConfig ProviderConfig
    65  }
    66  
    67  // ParseAbsProviderConfig parses the given traversal as an absolute provider
    68  // address. The following are examples of traversals that can be successfully
    69  // parsed as absolute provider configuration addresses:
    70  //
    71  //     provider.aws
    72  //     provider.aws.foo
    73  //     module.bar.provider.aws
    74  //     module.bar.module.baz.provider.aws.foo
    75  //     module.foo[1].provider.aws.foo
    76  //
    77  // This type of address is used, for example, to record the relationships
    78  // between resources and provider configurations in the state structure.
    79  // This type of address is not generally used in the UI, except in error
    80  // messages that refer to provider configurations.
    81  func ParseAbsProviderConfig(traversal hcl.Traversal) (AbsProviderConfig, tfdiags.Diagnostics) {
    82  	modInst, remain, diags := parseModuleInstancePrefix(traversal)
    83  	ret := AbsProviderConfig{
    84  		Module: modInst,
    85  	}
    86  	if len(remain) < 2 || remain.RootName() != "provider" {
    87  		diags = diags.Append(&hcl.Diagnostic{
    88  			Severity: hcl.DiagError,
    89  			Summary:  "Invalid provider configuration address",
    90  			Detail:   "Provider address must begin with \"provider.\", followed by a provider type name.",
    91  			Subject:  remain.SourceRange().Ptr(),
    92  		})
    93  		return ret, diags
    94  	}
    95  	if len(remain) > 3 {
    96  		diags = diags.Append(&hcl.Diagnostic{
    97  			Severity: hcl.DiagError,
    98  			Summary:  "Invalid provider configuration address",
    99  			Detail:   "Extraneous operators after provider configuration alias.",
   100  			Subject:  hcl.Traversal(remain[3:]).SourceRange().Ptr(),
   101  		})
   102  		return ret, diags
   103  	}
   104  
   105  	if tt, ok := remain[1].(hcl.TraverseAttr); ok {
   106  		ret.ProviderConfig.Type = NewLegacyProvider(tt.Name)
   107  	} else {
   108  		diags = diags.Append(&hcl.Diagnostic{
   109  			Severity: hcl.DiagError,
   110  			Summary:  "Invalid provider configuration address",
   111  			Detail:   "The prefix \"provider.\" must be followed by a provider type name.",
   112  			Subject:  remain[1].SourceRange().Ptr(),
   113  		})
   114  		return ret, diags
   115  	}
   116  
   117  	if len(remain) == 3 {
   118  		if tt, ok := remain[2].(hcl.TraverseAttr); ok {
   119  			ret.ProviderConfig.Alias = tt.Name
   120  		} else {
   121  			diags = diags.Append(&hcl.Diagnostic{
   122  				Severity: hcl.DiagError,
   123  				Summary:  "Invalid provider configuration address",
   124  				Detail:   "Provider type name must be followed by a configuration alias name.",
   125  				Subject:  remain[2].SourceRange().Ptr(),
   126  			})
   127  			return ret, diags
   128  		}
   129  	}
   130  
   131  	return ret, diags
   132  }
   133  
   134  // ParseAbsProviderConfigStr is a helper wrapper around ParseAbsProviderConfig
   135  // that takes a string and parses it with the HCL native syntax traversal parser
   136  // before interpreting it.
   137  //
   138  // This should be used only in specialized situations since it will cause the
   139  // created references to not have any meaningful source location information.
   140  // If a reference string is coming from a source that should be identified in
   141  // error messages then the caller should instead parse it directly using a
   142  // suitable function from the HCL API and pass the traversal itself to
   143  // ParseAbsProviderConfig.
   144  //
   145  // Error diagnostics are returned if either the parsing fails or the analysis
   146  // of the traversal fails. There is no way for the caller to distinguish the
   147  // two kinds of diagnostics programmatically. If error diagnostics are returned
   148  // the returned address is invalid.
   149  func ParseAbsProviderConfigStr(str string) (AbsProviderConfig, tfdiags.Diagnostics) {
   150  	var diags tfdiags.Diagnostics
   151  
   152  	traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
   153  	diags = diags.Append(parseDiags)
   154  	if parseDiags.HasErrors() {
   155  		return AbsProviderConfig{}, diags
   156  	}
   157  
   158  	addr, addrDiags := ParseAbsProviderConfig(traversal)
   159  	diags = diags.Append(addrDiags)
   160  	return addr, diags
   161  }
   162  
   163  // ProviderConfigDefault returns the address of the default provider config
   164  // of the given type inside the recieving module instance.
   165  func (m ModuleInstance) ProviderConfigDefault(name string) AbsProviderConfig {
   166  	return AbsProviderConfig{
   167  		Module: m,
   168  		ProviderConfig: ProviderConfig{
   169  			Type: NewLegacyProvider(name),
   170  		},
   171  	}
   172  }
   173  
   174  // ProviderConfigAliased returns the address of an aliased provider config
   175  // of with given type and alias inside the recieving module instance.
   176  func (m ModuleInstance) ProviderConfigAliased(name, alias string) AbsProviderConfig {
   177  	return AbsProviderConfig{
   178  		Module: m,
   179  		ProviderConfig: ProviderConfig{
   180  			Type:  NewLegacyProvider(name),
   181  			Alias: alias,
   182  		},
   183  	}
   184  }
   185  
   186  // Inherited returns an address that the receiving configuration address might
   187  // inherit from in a parent module. The second bool return value indicates if
   188  // such inheritance is possible, and thus whether the returned address is valid.
   189  //
   190  // Inheritance is possible only for default (un-aliased) providers in modules
   191  // other than the root module. Even if a valid address is returned, inheritence
   192  // may not be performed for other reasons, such as if the calling module
   193  // provided explicit provider configurations within the call for this module.
   194  // The ProviderTransformer graph transform in the main terraform module has
   195  // the authoritative logic for provider inheritance, and this method is here
   196  // mainly just for its benefit.
   197  func (pc AbsProviderConfig) Inherited() (AbsProviderConfig, bool) {
   198  	// Can't inherit if we're already in the root.
   199  	if len(pc.Module) == 0 {
   200  		return AbsProviderConfig{}, false
   201  	}
   202  
   203  	// Can't inherit if we have an alias.
   204  	if pc.ProviderConfig.Alias != "" {
   205  		return AbsProviderConfig{}, false
   206  	}
   207  
   208  	// Otherwise, we might inherit from a configuration with the same
   209  	// provider name in the parent module instance.
   210  	parentMod := pc.Module.Parent()
   211  	return pc.ProviderConfig.Absolute(parentMod), true
   212  }
   213  
   214  func (pc AbsProviderConfig) String() string {
   215  	if len(pc.Module) == 0 {
   216  		return pc.ProviderConfig.String()
   217  	}
   218  	return fmt.Sprintf("%s.%s", pc.Module.String(), pc.ProviderConfig.String())
   219  }