github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/addrs/resource.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package addrs
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  )
    10  
    11  // Resource is an address for a resource block within configuration, which
    12  // contains potentially-multiple resource instances if that configuration
    13  // block uses "count" or "for_each".
    14  type Resource struct {
    15  	referenceable
    16  	Mode ResourceMode
    17  	Type string
    18  	Name string
    19  }
    20  
    21  func (r Resource) String() string {
    22  	switch r.Mode {
    23  	case ManagedResourceMode:
    24  		return fmt.Sprintf("%s.%s", r.Type, r.Name)
    25  	case DataResourceMode:
    26  		return fmt.Sprintf("data.%s.%s", r.Type, r.Name)
    27  	default:
    28  		// Should never happen, but we'll return a string here rather than
    29  		// crashing just in case it does.
    30  		return fmt.Sprintf("<invalid>.%s.%s", r.Type, r.Name)
    31  	}
    32  }
    33  
    34  func (r Resource) Equal(o Resource) bool {
    35  	return r.Mode == o.Mode && r.Name == o.Name && r.Type == o.Type
    36  }
    37  
    38  func (r Resource) Less(o Resource) bool {
    39  	switch {
    40  	case r.Mode != o.Mode:
    41  		return r.Mode == DataResourceMode
    42  
    43  	case r.Type != o.Type:
    44  		return r.Type < o.Type
    45  
    46  	case r.Name != o.Name:
    47  		return r.Name < o.Name
    48  
    49  	default:
    50  		return false
    51  	}
    52  }
    53  
    54  func (r Resource) UniqueKey() UniqueKey {
    55  	return r // A Resource is its own UniqueKey
    56  }
    57  
    58  func (r Resource) uniqueKeySigil() {}
    59  
    60  // Instance produces the address for a specific instance of the receiver
    61  // that is idenfied by the given key.
    62  func (r Resource) Instance(key InstanceKey) ResourceInstance {
    63  	return ResourceInstance{
    64  		Resource: r,
    65  		Key:      key,
    66  	}
    67  }
    68  
    69  // Absolute returns an AbsResource from the receiver and the given module
    70  // instance address.
    71  func (r Resource) Absolute(module ModuleInstance) AbsResource {
    72  	return AbsResource{
    73  		Module:   module,
    74  		Resource: r,
    75  	}
    76  }
    77  
    78  // InModule returns a ConfigResource from the receiver and the given module
    79  // address.
    80  func (r Resource) InModule(module Module) ConfigResource {
    81  	return ConfigResource{
    82  		Module:   module,
    83  		Resource: r,
    84  	}
    85  }
    86  
    87  // ImpliedProvider returns the implied provider type name, for e.g. the "aws" in
    88  // "aws_instance"
    89  func (r Resource) ImpliedProvider() string {
    90  	typeName := r.Type
    91  	if under := strings.Index(typeName, "_"); under != -1 {
    92  		typeName = typeName[:under]
    93  	}
    94  
    95  	return typeName
    96  }
    97  
    98  // ResourceInstance is an address for a specific instance of a resource.
    99  // When a resource is defined in configuration with "count" or "for_each" it
   100  // produces zero or more instances, which can be addressed using this type.
   101  type ResourceInstance struct {
   102  	referenceable
   103  	Resource Resource
   104  	Key      InstanceKey
   105  }
   106  
   107  func (r ResourceInstance) ContainingResource() Resource {
   108  	return r.Resource
   109  }
   110  
   111  func (r ResourceInstance) String() string {
   112  	if r.Key == NoKey {
   113  		return r.Resource.String()
   114  	}
   115  	return r.Resource.String() + r.Key.String()
   116  }
   117  
   118  func (r ResourceInstance) Equal(o ResourceInstance) bool {
   119  	return r.Key == o.Key && r.Resource.Equal(o.Resource)
   120  }
   121  
   122  func (r ResourceInstance) Less(o ResourceInstance) bool {
   123  	if !r.Resource.Equal(o.Resource) {
   124  		return r.Resource.Less(o.Resource)
   125  	}
   126  
   127  	if r.Key != o.Key {
   128  		return InstanceKeyLess(r.Key, o.Key)
   129  	}
   130  
   131  	return false
   132  }
   133  
   134  func (r ResourceInstance) UniqueKey() UniqueKey {
   135  	return r // A ResourceInstance is its own UniqueKey
   136  }
   137  
   138  func (r ResourceInstance) uniqueKeySigil() {}
   139  
   140  // Absolute returns an AbsResourceInstance from the receiver and the given module
   141  // instance address.
   142  func (r ResourceInstance) Absolute(module ModuleInstance) AbsResourceInstance {
   143  	return AbsResourceInstance{
   144  		Module:   module,
   145  		Resource: r,
   146  	}
   147  }
   148  
   149  // AbsResource is an absolute address for a resource under a given module path.
   150  type AbsResource struct {
   151  	targetable
   152  	Module   ModuleInstance
   153  	Resource Resource
   154  }
   155  
   156  // Resource returns the address of a particular resource within the receiver.
   157  func (m ModuleInstance) Resource(mode ResourceMode, typeName string, name string) AbsResource {
   158  	return AbsResource{
   159  		Module: m,
   160  		Resource: Resource{
   161  			Mode: mode,
   162  			Type: typeName,
   163  			Name: name,
   164  		},
   165  	}
   166  }
   167  
   168  // Instance produces the address for a specific instance of the receiver
   169  // that is idenfied by the given key.
   170  func (r AbsResource) Instance(key InstanceKey) AbsResourceInstance {
   171  	return AbsResourceInstance{
   172  		Module:   r.Module,
   173  		Resource: r.Resource.Instance(key),
   174  	}
   175  }
   176  
   177  // Config returns the unexpanded ConfigResource for this AbsResource.
   178  func (r AbsResource) Config() ConfigResource {
   179  	return ConfigResource{
   180  		Module:   r.Module.Module(),
   181  		Resource: r.Resource,
   182  	}
   183  }
   184  
   185  // TargetContains implements Targetable by returning true if the given other
   186  // address is either equal to the receiver or is an instance of the
   187  // receiver.
   188  func (r AbsResource) TargetContains(other Targetable) bool {
   189  	switch to := other.(type) {
   190  
   191  	case AbsResource:
   192  		// We'll use our stringification as a cheat-ish way to test for equality.
   193  		return to.String() == r.String()
   194  
   195  	case ConfigResource:
   196  		// if an absolute resource from parsing a target address contains a
   197  		// ConfigResource, the string representation will match
   198  		return to.String() == r.String()
   199  
   200  	case AbsResourceInstance:
   201  		return r.TargetContains(to.ContainingResource())
   202  
   203  	default:
   204  		return false
   205  
   206  	}
   207  }
   208  
   209  func (r AbsResource) AddrType() TargetableAddrType {
   210  	return AbsResourceAddrType
   211  }
   212  
   213  func (r AbsResource) String() string {
   214  	if len(r.Module) == 0 {
   215  		return r.Resource.String()
   216  	}
   217  	return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
   218  }
   219  
   220  // AffectedAbsResource returns the AbsResource.
   221  func (r AbsResource) AffectedAbsResource() AbsResource {
   222  	return r
   223  }
   224  
   225  func (r AbsResource) Equal(o AbsResource) bool {
   226  	return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource)
   227  }
   228  
   229  func (r AbsResource) Less(o AbsResource) bool {
   230  	if !r.Module.Equal(o.Module) {
   231  		return r.Module.Less(o.Module)
   232  	}
   233  
   234  	if !r.Resource.Equal(o.Resource) {
   235  		return r.Resource.Less(o.Resource)
   236  	}
   237  
   238  	return false
   239  }
   240  
   241  func (r AbsResource) absMoveableSigil() {
   242  	// AbsResource is moveable
   243  }
   244  
   245  type absResourceKey string
   246  
   247  func (r absResourceKey) uniqueKeySigil() {}
   248  
   249  func (r AbsResource) UniqueKey() UniqueKey {
   250  	return absResourceKey(r.String())
   251  }
   252  
   253  // AbsResourceInstance is an absolute address for a resource instance under a
   254  // given module path.
   255  type AbsResourceInstance struct {
   256  	targetable
   257  	Module   ModuleInstance
   258  	Resource ResourceInstance
   259  }
   260  
   261  // ResourceInstance returns the address of a particular resource instance within the receiver.
   262  func (m ModuleInstance) ResourceInstance(mode ResourceMode, typeName string, name string, key InstanceKey) AbsResourceInstance {
   263  	return AbsResourceInstance{
   264  		Module: m,
   265  		Resource: ResourceInstance{
   266  			Resource: Resource{
   267  				Mode: mode,
   268  				Type: typeName,
   269  				Name: name,
   270  			},
   271  			Key: key,
   272  		},
   273  	}
   274  }
   275  
   276  // ContainingResource returns the address of the resource that contains the
   277  // receving resource instance. In other words, it discards the key portion
   278  // of the address to produce an AbsResource value.
   279  func (r AbsResourceInstance) ContainingResource() AbsResource {
   280  	return AbsResource{
   281  		Module:   r.Module,
   282  		Resource: r.Resource.ContainingResource(),
   283  	}
   284  }
   285  
   286  // ConfigResource returns the address of the configuration block that declared
   287  // this instance.
   288  func (r AbsResourceInstance) ConfigResource() ConfigResource {
   289  	return ConfigResource{
   290  		Module:   r.Module.Module(),
   291  		Resource: r.Resource.Resource,
   292  	}
   293  }
   294  
   295  // TargetContains implements Targetable by returning true if the given other
   296  // address is equal to the receiver.
   297  func (r AbsResourceInstance) TargetContains(other Targetable) bool {
   298  	switch to := other.(type) {
   299  
   300  	// while we currently don't start with an AbsResourceInstance as a target
   301  	// address, check all resource types for consistency.
   302  	case AbsResourceInstance:
   303  		// We'll use our stringification as a cheat-ish way to test for equality.
   304  		return to.String() == r.String()
   305  	case ConfigResource:
   306  		return to.String() == r.String()
   307  	case AbsResource:
   308  		return to.String() == r.String()
   309  
   310  	default:
   311  		return false
   312  
   313  	}
   314  }
   315  
   316  func (r AbsResourceInstance) AddrType() TargetableAddrType {
   317  	return AbsResourceInstanceAddrType
   318  }
   319  
   320  func (r AbsResourceInstance) String() string {
   321  	if len(r.Module) == 0 {
   322  		return r.Resource.String()
   323  	}
   324  	return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
   325  }
   326  
   327  // AffectedAbsResource returns the AbsResource for the instance.
   328  func (r AbsResourceInstance) AffectedAbsResource() AbsResource {
   329  	return AbsResource{
   330  		Module:   r.Module,
   331  		Resource: r.Resource.Resource,
   332  	}
   333  }
   334  
   335  func (r AbsResourceInstance) CheckRule(t CheckRuleType, i int) CheckRule {
   336  	return CheckRule{
   337  		Container: r,
   338  		Type:      t,
   339  		Index:     i,
   340  	}
   341  }
   342  
   343  func (v AbsResourceInstance) CheckableKind() CheckableKind {
   344  	return CheckableResource
   345  }
   346  
   347  func (r AbsResourceInstance) Equal(o AbsResourceInstance) bool {
   348  	return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource)
   349  }
   350  
   351  // Less returns true if the receiver should sort before the given other value
   352  // in a sorted list of addresses.
   353  func (r AbsResourceInstance) Less(o AbsResourceInstance) bool {
   354  	if !r.Module.Equal(o.Module) {
   355  		return r.Module.Less(o.Module)
   356  	}
   357  
   358  	if !r.Resource.Equal(o.Resource) {
   359  		return r.Resource.Less(o.Resource)
   360  	}
   361  
   362  	return false
   363  }
   364  
   365  // AbsResourceInstance is a Checkable
   366  func (r AbsResourceInstance) checkableSigil() {}
   367  
   368  func (r AbsResourceInstance) ConfigCheckable() ConfigCheckable {
   369  	// The ConfigCheckable for an AbsResourceInstance is its ConfigResource.
   370  	return r.ConfigResource()
   371  }
   372  
   373  type absResourceInstanceKey string
   374  
   375  func (r AbsResourceInstance) UniqueKey() UniqueKey {
   376  	return absResourceInstanceKey(r.String())
   377  }
   378  
   379  func (r absResourceInstanceKey) uniqueKeySigil() {}
   380  
   381  func (r AbsResourceInstance) absMoveableSigil() {
   382  	// AbsResourceInstance is moveable
   383  }
   384  
   385  // ConfigResource is an address for a resource within a configuration.
   386  type ConfigResource struct {
   387  	targetable
   388  	Module   Module
   389  	Resource Resource
   390  }
   391  
   392  // Resource returns the address of a particular resource within the module.
   393  func (m Module) Resource(mode ResourceMode, typeName string, name string) ConfigResource {
   394  	return ConfigResource{
   395  		Module: m,
   396  		Resource: Resource{
   397  			Mode: mode,
   398  			Type: typeName,
   399  			Name: name,
   400  		},
   401  	}
   402  }
   403  
   404  // Absolute produces the address for the receiver within a specific module instance.
   405  func (r ConfigResource) Absolute(module ModuleInstance) AbsResource {
   406  	return AbsResource{
   407  		Module:   module,
   408  		Resource: r.Resource,
   409  	}
   410  }
   411  
   412  // TargetContains implements Targetable by returning true if the given other
   413  // address is either equal to the receiver or is an instance of the
   414  // receiver.
   415  func (r ConfigResource) TargetContains(other Targetable) bool {
   416  	switch to := other.(type) {
   417  	case ConfigResource:
   418  		// We'll use our stringification as a cheat-ish way to test for equality.
   419  		return to.String() == r.String()
   420  	case AbsResource:
   421  		return r.TargetContains(to.Config())
   422  	case AbsResourceInstance:
   423  		return r.TargetContains(to.ContainingResource())
   424  	default:
   425  		return false
   426  	}
   427  }
   428  
   429  func (r ConfigResource) AddrType() TargetableAddrType {
   430  	return ConfigResourceAddrType
   431  }
   432  
   433  func (r ConfigResource) String() string {
   434  	if len(r.Module) == 0 {
   435  		return r.Resource.String()
   436  	}
   437  	return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
   438  }
   439  
   440  func (r ConfigResource) Equal(o ConfigResource) bool {
   441  	return r.Module.Equal(o.Module) && r.Resource.Equal(o.Resource)
   442  }
   443  
   444  func (r ConfigResource) UniqueKey() UniqueKey {
   445  	return configResourceKey(r.String())
   446  }
   447  
   448  func (r ConfigResource) configMoveableSigil() {
   449  	// ConfigResource is moveable
   450  }
   451  
   452  func (r ConfigResource) configCheckableSigil() {
   453  	// ConfigResource represents a configuration object that declares checkable objects
   454  }
   455  
   456  func (v ConfigResource) CheckableKind() CheckableKind {
   457  	return CheckableResource
   458  }
   459  
   460  type configResourceKey string
   461  
   462  func (k configResourceKey) uniqueKeySigil() {}
   463  
   464  // ResourceMode defines which lifecycle applies to a given resource. Each
   465  // resource lifecycle has a slightly different address format.
   466  type ResourceMode rune
   467  
   468  //go:generate go run golang.org/x/tools/cmd/stringer -type ResourceMode
   469  
   470  const (
   471  	// InvalidResourceMode is the zero value of ResourceMode and is not
   472  	// a valid resource mode.
   473  	InvalidResourceMode ResourceMode = 0
   474  
   475  	// ManagedResourceMode indicates a managed resource, as defined by
   476  	// "resource" blocks in configuration.
   477  	ManagedResourceMode ResourceMode = 'M'
   478  
   479  	// DataResourceMode indicates a data resource, as defined by
   480  	// "data" blocks in configuration.
   481  	DataResourceMode ResourceMode = 'D'
   482  )