github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/addrs/provider_config.go (about)

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