github.com/opentofu/opentofu@v1.7.1/internal/tofu/transform_orphan_resource_test.go (about)

     1  // Copyright (c) The OpenTofu Authors
     2  // SPDX-License-Identifier: MPL-2.0
     3  // Copyright (c) 2023 HashiCorp, Inc.
     4  // SPDX-License-Identifier: MPL-2.0
     5  
     6  package tofu
     7  
     8  import (
     9  	"fmt"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/opentofu/opentofu/internal/addrs"
    14  	"github.com/opentofu/opentofu/internal/dag"
    15  	"github.com/opentofu/opentofu/internal/states"
    16  )
    17  
    18  func TestOrphanResourceInstanceTransformer(t *testing.T) {
    19  	mod := testModule(t, "transform-orphan-basic")
    20  
    21  	state := states.BuildState(func(s *states.SyncState) {
    22  		s.SetResourceInstanceCurrent(
    23  			addrs.Resource{
    24  				Mode: addrs.ManagedResourceMode,
    25  				Type: "aws_instance",
    26  				Name: "web",
    27  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    28  			&states.ResourceInstanceObjectSrc{
    29  				AttrsFlat: map[string]string{
    30  					"id": "foo",
    31  				},
    32  				Status: states.ObjectReady,
    33  			},
    34  			addrs.AbsProviderConfig{
    35  				Provider: addrs.NewDefaultProvider("aws"),
    36  				Module:   addrs.RootModule,
    37  			},
    38  		)
    39  
    40  		// The orphan
    41  		s.SetResourceInstanceCurrent(
    42  			addrs.Resource{
    43  				Mode: addrs.ManagedResourceMode,
    44  				Type: "aws_instance",
    45  				Name: "db",
    46  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    47  			&states.ResourceInstanceObjectSrc{
    48  				AttrsFlat: map[string]string{
    49  					"id": "foo",
    50  				},
    51  				Status: states.ObjectReady,
    52  			},
    53  			addrs.AbsProviderConfig{
    54  				Provider: addrs.NewDefaultProvider("aws"),
    55  				Module:   addrs.RootModule,
    56  			},
    57  		)
    58  
    59  		// A deposed orphan should not be handled by this transformer
    60  		s.SetResourceInstanceDeposed(
    61  			addrs.Resource{
    62  				Mode: addrs.ManagedResourceMode,
    63  				Type: "test_instance",
    64  				Name: "deposed",
    65  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    66  			states.NewDeposedKey(),
    67  			&states.ResourceInstanceObjectSrc{
    68  				AttrsFlat: map[string]string{
    69  					"id": "foo",
    70  				},
    71  				Status: states.ObjectReady,
    72  			},
    73  			addrs.AbsProviderConfig{
    74  				Provider: addrs.NewDefaultProvider("test"),
    75  				Module:   addrs.RootModule,
    76  			},
    77  		)
    78  	})
    79  
    80  	g := Graph{Path: addrs.RootModuleInstance}
    81  	{
    82  		tf := &ConfigTransformer{Config: mod}
    83  		if err := tf.Transform(&g); err != nil {
    84  			t.Fatalf("err: %s", err)
    85  		}
    86  	}
    87  
    88  	{
    89  		tf := &OrphanResourceInstanceTransformer{
    90  			Concrete: testOrphanResourceConcreteFunc,
    91  			State:    state,
    92  			Config:   mod,
    93  		}
    94  		if err := tf.Transform(&g); err != nil {
    95  			t.Fatalf("err: %s", err)
    96  		}
    97  	}
    98  
    99  	actual := strings.TrimSpace(g.String())
   100  	expected := strings.TrimSpace(testTransformOrphanResourceBasicStr)
   101  	if actual != expected {
   102  		t.Fatalf("bad:\n\n%s", actual)
   103  	}
   104  }
   105  
   106  func TestOrphanResourceInstanceTransformer_countGood(t *testing.T) {
   107  	mod := testModule(t, "transform-orphan-count")
   108  
   109  	state := states.BuildState(func(s *states.SyncState) {
   110  		s.SetResourceInstanceCurrent(
   111  			addrs.Resource{
   112  				Mode: addrs.ManagedResourceMode,
   113  				Type: "aws_instance",
   114  				Name: "foo",
   115  			}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   116  			&states.ResourceInstanceObjectSrc{
   117  				AttrsFlat: map[string]string{
   118  					"id": "foo",
   119  				},
   120  				Status: states.ObjectReady,
   121  			},
   122  			addrs.AbsProviderConfig{
   123  				Provider: addrs.NewDefaultProvider("aws"),
   124  				Module:   addrs.RootModule,
   125  			},
   126  		)
   127  		s.SetResourceInstanceCurrent(
   128  			addrs.Resource{
   129  				Mode: addrs.ManagedResourceMode,
   130  				Type: "aws_instance",
   131  				Name: "foo",
   132  			}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance),
   133  			&states.ResourceInstanceObjectSrc{
   134  				AttrsFlat: map[string]string{
   135  					"id": "foo",
   136  				},
   137  				Status: states.ObjectReady,
   138  			},
   139  			addrs.AbsProviderConfig{
   140  				Provider: addrs.NewDefaultProvider("aws"),
   141  				Module:   addrs.RootModule,
   142  			},
   143  		)
   144  	})
   145  
   146  	g := Graph{Path: addrs.RootModuleInstance}
   147  	{
   148  		tf := &ConfigTransformer{Config: mod}
   149  		if err := tf.Transform(&g); err != nil {
   150  			t.Fatalf("err: %s", err)
   151  		}
   152  	}
   153  
   154  	{
   155  		tf := &OrphanResourceInstanceTransformer{
   156  			Concrete: testOrphanResourceConcreteFunc,
   157  			State:    state,
   158  			Config:   mod,
   159  		}
   160  		if err := tf.Transform(&g); err != nil {
   161  			t.Fatalf("err: %s", err)
   162  		}
   163  	}
   164  
   165  	actual := strings.TrimSpace(g.String())
   166  	expected := strings.TrimSpace(testTransformOrphanResourceCountStr)
   167  	if actual != expected {
   168  		t.Fatalf("bad:\n\n%s", actual)
   169  	}
   170  }
   171  
   172  func TestOrphanResourceInstanceTransformer_countBad(t *testing.T) {
   173  	mod := testModule(t, "transform-orphan-count-empty")
   174  	state := states.BuildState(func(s *states.SyncState) {
   175  		s.SetResourceInstanceCurrent(
   176  			addrs.Resource{
   177  				Mode: addrs.ManagedResourceMode,
   178  				Type: "aws_instance",
   179  				Name: "foo",
   180  			}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   181  			&states.ResourceInstanceObjectSrc{
   182  				AttrsFlat: map[string]string{
   183  					"id": "foo",
   184  				},
   185  				Status: states.ObjectReady,
   186  			},
   187  			addrs.AbsProviderConfig{
   188  				Provider: addrs.NewDefaultProvider("aws"),
   189  				Module:   addrs.RootModule,
   190  			},
   191  		)
   192  		s.SetResourceInstanceCurrent(
   193  			addrs.Resource{
   194  				Mode: addrs.ManagedResourceMode,
   195  				Type: "aws_instance",
   196  				Name: "foo",
   197  			}.Instance(addrs.IntKey(1)).Absolute(addrs.RootModuleInstance),
   198  			&states.ResourceInstanceObjectSrc{
   199  				AttrsFlat: map[string]string{
   200  					"id": "foo",
   201  				},
   202  				Status: states.ObjectReady,
   203  			},
   204  			addrs.AbsProviderConfig{
   205  				Provider: addrs.NewDefaultProvider("aws"),
   206  				Module:   addrs.RootModule,
   207  			},
   208  		)
   209  	})
   210  
   211  	g := Graph{Path: addrs.RootModuleInstance}
   212  	{
   213  		tf := &ConfigTransformer{Config: mod}
   214  		if err := tf.Transform(&g); err != nil {
   215  			t.Fatalf("err: %s", err)
   216  		}
   217  	}
   218  
   219  	{
   220  		tf := &OrphanResourceInstanceTransformer{
   221  			Concrete: testOrphanResourceConcreteFunc,
   222  			State:    state,
   223  			Config:   mod,
   224  		}
   225  		if err := tf.Transform(&g); err != nil {
   226  			t.Fatalf("err: %s", err)
   227  		}
   228  	}
   229  
   230  	actual := strings.TrimSpace(g.String())
   231  	expected := strings.TrimSpace(testTransformOrphanResourceCountBadStr)
   232  	if actual != expected {
   233  		t.Fatalf("bad:\n\n%s", actual)
   234  	}
   235  }
   236  
   237  func TestOrphanResourceInstanceTransformer_modules(t *testing.T) {
   238  	mod := testModule(t, "transform-orphan-modules")
   239  	state := states.BuildState(func(s *states.SyncState) {
   240  		s.SetResourceInstanceCurrent(
   241  			addrs.Resource{
   242  				Mode: addrs.ManagedResourceMode,
   243  				Type: "aws_instance",
   244  				Name: "foo",
   245  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   246  			&states.ResourceInstanceObjectSrc{
   247  				AttrsFlat: map[string]string{
   248  					"id": "foo",
   249  				},
   250  				Status: states.ObjectReady,
   251  			},
   252  			addrs.AbsProviderConfig{
   253  				Provider: addrs.NewDefaultProvider("aws"),
   254  				Module:   addrs.RootModule,
   255  			},
   256  		)
   257  		s.SetResourceInstanceCurrent(
   258  			addrs.Resource{
   259  				Mode: addrs.ManagedResourceMode,
   260  				Type: "aws_instance",
   261  				Name: "web",
   262  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey)),
   263  			&states.ResourceInstanceObjectSrc{
   264  				AttrsFlat: map[string]string{
   265  					"id": "foo",
   266  				},
   267  				Status: states.ObjectReady,
   268  			},
   269  			addrs.AbsProviderConfig{
   270  				Provider: addrs.NewDefaultProvider("aws"),
   271  				Module:   addrs.RootModule,
   272  			},
   273  		)
   274  	})
   275  
   276  	g := Graph{Path: addrs.RootModuleInstance}
   277  	{
   278  		tf := &ConfigTransformer{Config: mod}
   279  		if err := tf.Transform(&g); err != nil {
   280  			t.Fatalf("err: %s", err)
   281  		}
   282  	}
   283  
   284  	{
   285  		tf := &OrphanResourceInstanceTransformer{
   286  			Concrete: testOrphanResourceConcreteFunc,
   287  			State:    state,
   288  			Config:   mod,
   289  		}
   290  		if err := tf.Transform(&g); err != nil {
   291  			t.Fatalf("err: %s", err)
   292  		}
   293  	}
   294  
   295  	got := strings.TrimSpace(g.String())
   296  	want := strings.TrimSpace(testTransformOrphanResourceModulesStr)
   297  	if got != want {
   298  		t.Fatalf("wrong state result\ngot:\n%s\n\nwant:\n%s", got, want)
   299  	}
   300  }
   301  
   302  const testTransformOrphanResourceBasicStr = `
   303  aws_instance.db (orphan)
   304  aws_instance.web
   305  `
   306  
   307  const testTransformOrphanResourceCountStr = `
   308  aws_instance.foo
   309  `
   310  
   311  const testTransformOrphanResourceCountBadStr = `
   312  aws_instance.foo[0] (orphan)
   313  aws_instance.foo[1] (orphan)
   314  `
   315  
   316  const testTransformOrphanResourceModulesStr = `
   317  aws_instance.foo
   318  module.child.aws_instance.web (orphan)
   319  `
   320  
   321  func testOrphanResourceConcreteFunc(a *NodeAbstractResourceInstance) dag.Vertex {
   322  	return &testOrphanResourceInstanceConcrete{a}
   323  }
   324  
   325  type testOrphanResourceInstanceConcrete struct {
   326  	*NodeAbstractResourceInstance
   327  }
   328  
   329  func (n *testOrphanResourceInstanceConcrete) Name() string {
   330  	return fmt.Sprintf("%s (orphan)", n.NodeAbstractResourceInstance.Name())
   331  }