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

     1  package terraform
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/terraform/addrs"
     8  	"github.com/hashicorp/terraform/states"
     9  	"github.com/zclconf/go-cty/cty"
    10  )
    11  
    12  func TestOrphanResourceCountTransformer(t *testing.T) {
    13  	state := MustShimLegacyState(&State{
    14  		Modules: []*ModuleState{
    15  			&ModuleState{
    16  				Path: []string{"root"},
    17  				Resources: map[string]*ResourceState{
    18  					"aws_instance.web": &ResourceState{
    19  						Type: "aws_instance",
    20  						Primary: &InstanceState{
    21  							ID: "foo",
    22  						},
    23  					},
    24  
    25  					"aws_instance.foo": &ResourceState{
    26  						Type: "aws_instance",
    27  						Primary: &InstanceState{
    28  							ID: "foo",
    29  						},
    30  					},
    31  
    32  					"aws_instance.foo.2": &ResourceState{
    33  						Type: "aws_instance",
    34  						Primary: &InstanceState{
    35  							ID: "foo",
    36  						},
    37  					},
    38  				},
    39  			},
    40  		},
    41  	})
    42  
    43  	g := Graph{Path: addrs.RootModuleInstance}
    44  
    45  	{
    46  		tf := &OrphanResourceCountTransformer{
    47  			Concrete: testOrphanResourceConcreteFunc,
    48  			Count:    1,
    49  			Addr: addrs.RootModuleInstance.Resource(
    50  				addrs.ManagedResourceMode, "aws_instance", "foo",
    51  			),
    52  			State: state,
    53  		}
    54  		if err := tf.Transform(&g); err != nil {
    55  			t.Fatalf("err: %s", err)
    56  		}
    57  	}
    58  
    59  	actual := strings.TrimSpace(g.String())
    60  	expected := strings.TrimSpace(testTransformOrphanResourceCountBasicStr)
    61  	if actual != expected {
    62  		t.Fatalf("bad:\n\n%s", actual)
    63  	}
    64  }
    65  
    66  func TestOrphanResourceCountTransformer_zero(t *testing.T) {
    67  	state := MustShimLegacyState(&State{
    68  		Modules: []*ModuleState{
    69  			&ModuleState{
    70  				Path: []string{"root"},
    71  				Resources: map[string]*ResourceState{
    72  					"aws_instance.web": &ResourceState{
    73  						Type: "aws_instance",
    74  						Primary: &InstanceState{
    75  							ID: "foo",
    76  						},
    77  					},
    78  
    79  					"aws_instance.foo": &ResourceState{
    80  						Type: "aws_instance",
    81  						Primary: &InstanceState{
    82  							ID: "foo",
    83  						},
    84  					},
    85  
    86  					"aws_instance.foo.2": &ResourceState{
    87  						Type: "aws_instance",
    88  						Primary: &InstanceState{
    89  							ID: "foo",
    90  						},
    91  					},
    92  				},
    93  			},
    94  		},
    95  	})
    96  
    97  	g := Graph{Path: addrs.RootModuleInstance}
    98  
    99  	{
   100  		tf := &OrphanResourceCountTransformer{
   101  			Concrete: testOrphanResourceConcreteFunc,
   102  			Count:    0,
   103  			Addr: addrs.RootModuleInstance.Resource(
   104  				addrs.ManagedResourceMode, "aws_instance", "foo",
   105  			),
   106  			State: state,
   107  		}
   108  		if err := tf.Transform(&g); err != nil {
   109  			t.Fatalf("err: %s", err)
   110  		}
   111  	}
   112  
   113  	actual := strings.TrimSpace(g.String())
   114  	expected := strings.TrimSpace(testTransformOrphanResourceCountZeroStr)
   115  	if actual != expected {
   116  		t.Fatalf("bad:\n\n%s", actual)
   117  	}
   118  }
   119  
   120  func TestOrphanResourceCountTransformer_oneNoIndex(t *testing.T) {
   121  	state := MustShimLegacyState(&State{
   122  		Modules: []*ModuleState{
   123  			&ModuleState{
   124  				Path: []string{"root"},
   125  				Resources: map[string]*ResourceState{
   126  					"aws_instance.web": &ResourceState{
   127  						Type: "aws_instance",
   128  						Primary: &InstanceState{
   129  							ID: "foo",
   130  						},
   131  					},
   132  
   133  					"aws_instance.foo": &ResourceState{
   134  						Type: "aws_instance",
   135  						Primary: &InstanceState{
   136  							ID: "foo",
   137  						},
   138  					},
   139  
   140  					"aws_instance.foo.2": &ResourceState{
   141  						Type: "aws_instance",
   142  						Primary: &InstanceState{
   143  							ID: "foo",
   144  						},
   145  					},
   146  				},
   147  			},
   148  		},
   149  	})
   150  
   151  	g := Graph{Path: addrs.RootModuleInstance}
   152  
   153  	{
   154  		tf := &OrphanResourceCountTransformer{
   155  			Concrete: testOrphanResourceConcreteFunc,
   156  			Count:    1,
   157  			Addr: addrs.RootModuleInstance.Resource(
   158  				addrs.ManagedResourceMode, "aws_instance", "foo",
   159  			),
   160  			State: state,
   161  		}
   162  		if err := tf.Transform(&g); err != nil {
   163  			t.Fatalf("err: %s", err)
   164  		}
   165  	}
   166  
   167  	actual := strings.TrimSpace(g.String())
   168  	expected := strings.TrimSpace(testTransformOrphanResourceCountOneNoIndexStr)
   169  	if actual != expected {
   170  		t.Fatalf("bad:\n\n%s", actual)
   171  	}
   172  }
   173  
   174  func TestOrphanResourceCountTransformer_oneIndex(t *testing.T) {
   175  	state := MustShimLegacyState(&State{
   176  		Modules: []*ModuleState{
   177  			&ModuleState{
   178  				Path: []string{"root"},
   179  				Resources: map[string]*ResourceState{
   180  					"aws_instance.web": &ResourceState{
   181  						Type: "aws_instance",
   182  						Primary: &InstanceState{
   183  							ID: "foo",
   184  						},
   185  					},
   186  
   187  					"aws_instance.foo.0": &ResourceState{
   188  						Type: "aws_instance",
   189  						Primary: &InstanceState{
   190  							ID: "foo",
   191  						},
   192  					},
   193  
   194  					"aws_instance.foo.1": &ResourceState{
   195  						Type: "aws_instance",
   196  						Primary: &InstanceState{
   197  							ID: "foo",
   198  						},
   199  					},
   200  				},
   201  			},
   202  		},
   203  	})
   204  
   205  	g := Graph{Path: addrs.RootModuleInstance}
   206  
   207  	{
   208  		tf := &OrphanResourceCountTransformer{
   209  			Concrete: testOrphanResourceConcreteFunc,
   210  			Count:    1,
   211  			Addr: addrs.RootModuleInstance.Resource(
   212  				addrs.ManagedResourceMode, "aws_instance", "foo",
   213  			),
   214  			State: state,
   215  		}
   216  		if err := tf.Transform(&g); err != nil {
   217  			t.Fatalf("err: %s", err)
   218  		}
   219  	}
   220  
   221  	actual := strings.TrimSpace(g.String())
   222  	expected := strings.TrimSpace(testTransformOrphanResourceCountOneIndexStr)
   223  	if actual != expected {
   224  		t.Fatalf("bad:\n\n%s", actual)
   225  	}
   226  }
   227  
   228  func TestOrphanResourceCountTransformer_zeroAndNone(t *testing.T) {
   229  	state := MustShimLegacyState(&State{
   230  		Modules: []*ModuleState{
   231  			&ModuleState{
   232  				Path: []string{"root"},
   233  				Resources: map[string]*ResourceState{
   234  					"aws_instance.web": &ResourceState{
   235  						Type: "aws_instance",
   236  						Primary: &InstanceState{
   237  							ID: "foo",
   238  						},
   239  					},
   240  
   241  					"aws_instance.foo": &ResourceState{
   242  						Type: "aws_instance",
   243  						Primary: &InstanceState{
   244  							ID: "foo",
   245  						},
   246  					},
   247  
   248  					"aws_instance.foo.0": &ResourceState{
   249  						Type: "aws_instance",
   250  						Primary: &InstanceState{
   251  							ID: "foo",
   252  						},
   253  					},
   254  				},
   255  			},
   256  		},
   257  	})
   258  
   259  	g := Graph{Path: addrs.RootModuleInstance}
   260  
   261  	{
   262  		tf := &OrphanResourceCountTransformer{
   263  			Concrete: testOrphanResourceConcreteFunc,
   264  			Count:    -1,
   265  			Addr: addrs.RootModuleInstance.Resource(
   266  				addrs.ManagedResourceMode, "aws_instance", "foo",
   267  			),
   268  			State: state,
   269  		}
   270  		if err := tf.Transform(&g); err != nil {
   271  			t.Fatalf("err: %s", err)
   272  		}
   273  	}
   274  
   275  	actual := strings.TrimSpace(g.String())
   276  	expected := strings.TrimSpace(testTransformOrphanResourceCountZeroAndNoneStr)
   277  	if actual != expected {
   278  		t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
   279  	}
   280  }
   281  
   282  func TestOrphanResourceCountTransformer_zeroAndNoneCount(t *testing.T) {
   283  	state := MustShimLegacyState(&State{
   284  		Modules: []*ModuleState{
   285  			&ModuleState{
   286  				Path: []string{"root"},
   287  				Resources: map[string]*ResourceState{
   288  					"aws_instance.web": &ResourceState{
   289  						Type: "aws_instance",
   290  						Primary: &InstanceState{
   291  							ID: "foo",
   292  						},
   293  					},
   294  
   295  					"aws_instance.foo": &ResourceState{
   296  						Type: "aws_instance",
   297  						Primary: &InstanceState{
   298  							ID: "foo",
   299  						},
   300  					},
   301  
   302  					"aws_instance.foo.0": &ResourceState{
   303  						Type: "aws_instance",
   304  						Primary: &InstanceState{
   305  							ID: "foo",
   306  						},
   307  					},
   308  				},
   309  			},
   310  		},
   311  	})
   312  
   313  	g := Graph{Path: addrs.RootModuleInstance}
   314  
   315  	{
   316  		tf := &OrphanResourceCountTransformer{
   317  			Concrete: testOrphanResourceConcreteFunc,
   318  			Count:    2,
   319  			Addr: addrs.RootModuleInstance.Resource(
   320  				addrs.ManagedResourceMode, "aws_instance", "foo",
   321  			),
   322  			State: state,
   323  		}
   324  		if err := tf.Transform(&g); err != nil {
   325  			t.Fatalf("err: %s", err)
   326  		}
   327  	}
   328  
   329  	actual := strings.TrimSpace(g.String())
   330  	expected := strings.TrimSpace(testTransformOrphanResourceCountZeroAndNoneCountStr)
   331  	if actual != expected {
   332  		t.Fatalf("bad:\n\n%s", actual)
   333  	}
   334  }
   335  
   336  // When converting from a NoEach mode to an EachMap via a switch to for_each,
   337  // an edge is necessary to ensure that the map-key'd instances
   338  // are evaluated after the NoKey resource, because the final instance evaluated
   339  // sets the whole resource's EachMode.
   340  func TestOrphanResourceCountTransformer_ForEachEdgesAdded(t *testing.T) {
   341  	state := states.BuildState(func(s *states.SyncState) {
   342  		// "bar" key'd resource
   343  		s.SetResourceInstanceCurrent(
   344  			addrs.Resource{
   345  				Mode: addrs.ManagedResourceMode,
   346  				Type: "aws_instance",
   347  				Name: "foo",
   348  			}.Instance(addrs.StringKey("bar")).Absolute(addrs.RootModuleInstance),
   349  			&states.ResourceInstanceObjectSrc{
   350  				AttrsFlat: map[string]string{
   351  					"id": "foo",
   352  				},
   353  				Status: states.ObjectReady,
   354  			},
   355  			addrs.ProviderConfig{
   356  				Type: addrs.NewLegacyProvider("aws"),
   357  			}.Absolute(addrs.RootModuleInstance),
   358  		)
   359  
   360  		// NoKey'd resource
   361  		s.SetResourceInstanceCurrent(
   362  			addrs.Resource{
   363  				Mode: addrs.ManagedResourceMode,
   364  				Type: "aws_instance",
   365  				Name: "foo",
   366  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   367  			&states.ResourceInstanceObjectSrc{
   368  				AttrsFlat: map[string]string{
   369  					"id": "foo",
   370  				},
   371  				Status: states.ObjectReady,
   372  			},
   373  			addrs.ProviderConfig{
   374  				Type: addrs.NewLegacyProvider("aws"),
   375  			}.Absolute(addrs.RootModuleInstance),
   376  		)
   377  	})
   378  
   379  	g := Graph{Path: addrs.RootModuleInstance}
   380  
   381  	{
   382  		tf := &OrphanResourceCountTransformer{
   383  			Concrete: testOrphanResourceConcreteFunc,
   384  			// No keys in this ForEach ensure both our resources end
   385  			// up orphaned in this test
   386  			ForEach: map[string]cty.Value{},
   387  			Addr: addrs.RootModuleInstance.Resource(
   388  				addrs.ManagedResourceMode, "aws_instance", "foo",
   389  			),
   390  			State: state,
   391  		}
   392  		if err := tf.Transform(&g); err != nil {
   393  			t.Fatalf("err: %s", err)
   394  		}
   395  	}
   396  
   397  	actual := strings.TrimSpace(g.String())
   398  	expected := strings.TrimSpace(testTransformOrphanResourceForEachStr)
   399  	if actual != expected {
   400  		t.Fatalf("bad:\n\n%s", actual)
   401  	}
   402  }
   403  
   404  const testTransformOrphanResourceCountBasicStr = `
   405  aws_instance.foo[2] (orphan)
   406  `
   407  
   408  const testTransformOrphanResourceCountZeroStr = `
   409  aws_instance.foo (orphan)
   410  aws_instance.foo[2] (orphan)
   411  `
   412  
   413  const testTransformOrphanResourceCountOneNoIndexStr = `
   414  aws_instance.foo[2] (orphan)
   415  `
   416  
   417  const testTransformOrphanResourceCountOneIndexStr = `
   418  aws_instance.foo[1] (orphan)
   419  `
   420  
   421  const testTransformOrphanResourceCountZeroAndNoneStr = `
   422  aws_instance.foo[0] (orphan)
   423  `
   424  
   425  const testTransformOrphanResourceCountZeroAndNoneCountStr = `
   426  aws_instance.foo (orphan)
   427  `
   428  
   429  const testTransformOrphanResourceForEachStr = `
   430  aws_instance.foo (orphan)
   431  aws_instance.foo["bar"] (orphan)
   432    aws_instance.foo (orphan)
   433  `