github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/views/hook_json_test.go (about)

     1  package views
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/internal/addrs"
    10  	"github.com/hashicorp/terraform/internal/plans"
    11  	"github.com/hashicorp/terraform/internal/states"
    12  	"github.com/hashicorp/terraform/internal/terminal"
    13  	"github.com/hashicorp/terraform/internal/terraform"
    14  	"github.com/zclconf/go-cty/cty"
    15  )
    16  
    17  // Test a sequence of hooks associated with creating a resource
    18  func TestJSONHook_create(t *testing.T) {
    19  	streams, done := terminal.StreamsForTesting(t)
    20  	hook := newJSONHook(NewJSONView(NewView(streams)))
    21  
    22  	var nowMu sync.Mutex
    23  	now := time.Now()
    24  	hook.timeNow = func() time.Time {
    25  		nowMu.Lock()
    26  		defer nowMu.Unlock()
    27  		return now
    28  	}
    29  
    30  	after := make(chan time.Time, 1)
    31  	hook.timeAfter = func(time.Duration) <-chan time.Time { return after }
    32  
    33  	addr := addrs.Resource{
    34  		Mode: addrs.ManagedResourceMode,
    35  		Type: "test_instance",
    36  		Name: "boop",
    37  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
    38  	priorState := cty.NullVal(cty.Object(map[string]cty.Type{
    39  		"id":  cty.String,
    40  		"bar": cty.List(cty.String),
    41  	}))
    42  	plannedNewState := cty.ObjectVal(map[string]cty.Value{
    43  		"id": cty.StringVal("test"),
    44  		"bar": cty.ListVal([]cty.Value{
    45  			cty.StringVal("baz"),
    46  		}),
    47  	})
    48  
    49  	action, err := hook.PreApply(addr, states.CurrentGen, plans.Create, priorState, plannedNewState)
    50  	testHookReturnValues(t, action, err)
    51  
    52  	action, err = hook.PreProvisionInstanceStep(addr, "local-exec")
    53  	testHookReturnValues(t, action, err)
    54  
    55  	hook.ProvisionOutput(addr, "local-exec", `Executing: ["/bin/sh" "-c" "touch /etc/motd"]`)
    56  
    57  	action, err = hook.PostProvisionInstanceStep(addr, "local-exec", nil)
    58  	testHookReturnValues(t, action, err)
    59  
    60  	// Travel 10s into the future, notify the progress goroutine, and sleep
    61  	// briefly to allow it to execute
    62  	nowMu.Lock()
    63  	now = now.Add(10 * time.Second)
    64  	after <- now
    65  	nowMu.Unlock()
    66  	time.Sleep(1 * time.Millisecond)
    67  
    68  	// Travel 10s into the future, notify the progress goroutine, and sleep
    69  	// briefly to allow it to execute
    70  	nowMu.Lock()
    71  	now = now.Add(10 * time.Second)
    72  	after <- now
    73  	nowMu.Unlock()
    74  	time.Sleep(1 * time.Millisecond)
    75  
    76  	// Travel 2s into the future. We have arrived!
    77  	nowMu.Lock()
    78  	now = now.Add(2 * time.Second)
    79  	nowMu.Unlock()
    80  
    81  	action, err = hook.PostApply(addr, states.CurrentGen, plannedNewState, nil)
    82  	testHookReturnValues(t, action, err)
    83  
    84  	// Shut down the progress goroutine if still active
    85  	hook.applyingLock.Lock()
    86  	for key, progress := range hook.applying {
    87  		close(progress.done)
    88  		<-progress.heartbeatDone
    89  		delete(hook.applying, key)
    90  	}
    91  	hook.applyingLock.Unlock()
    92  
    93  	wantResource := map[string]interface{}{
    94  		"addr":             string("test_instance.boop"),
    95  		"implied_provider": string("test"),
    96  		"module":           string(""),
    97  		"resource":         string("test_instance.boop"),
    98  		"resource_key":     nil,
    99  		"resource_name":    string("boop"),
   100  		"resource_type":    string("test_instance"),
   101  	}
   102  	want := []map[string]interface{}{
   103  		{
   104  			"@level":   "info",
   105  			"@message": "test_instance.boop: Creating...",
   106  			"@module":  "terraform.ui",
   107  			"type":     "apply_start",
   108  			"hook": map[string]interface{}{
   109  				"action":   string("create"),
   110  				"resource": wantResource,
   111  			},
   112  		},
   113  		{
   114  			"@level":   "info",
   115  			"@message": "test_instance.boop: Provisioning with 'local-exec'...",
   116  			"@module":  "terraform.ui",
   117  			"type":     "provision_start",
   118  			"hook": map[string]interface{}{
   119  				"provisioner": "local-exec",
   120  				"resource":    wantResource,
   121  			},
   122  		},
   123  		{
   124  			"@level":   "info",
   125  			"@message": `test_instance.boop: (local-exec): Executing: ["/bin/sh" "-c" "touch /etc/motd"]`,
   126  			"@module":  "terraform.ui",
   127  			"type":     "provision_progress",
   128  			"hook": map[string]interface{}{
   129  				"output":      `Executing: ["/bin/sh" "-c" "touch /etc/motd"]`,
   130  				"provisioner": "local-exec",
   131  				"resource":    wantResource,
   132  			},
   133  		},
   134  		{
   135  			"@level":   "info",
   136  			"@message": "test_instance.boop: (local-exec) Provisioning complete",
   137  			"@module":  "terraform.ui",
   138  			"type":     "provision_complete",
   139  			"hook": map[string]interface{}{
   140  				"provisioner": "local-exec",
   141  				"resource":    wantResource,
   142  			},
   143  		},
   144  		{
   145  			"@level":   "info",
   146  			"@message": "test_instance.boop: Still creating... [10s elapsed]",
   147  			"@module":  "terraform.ui",
   148  			"type":     "apply_progress",
   149  			"hook": map[string]interface{}{
   150  				"action":          string("create"),
   151  				"elapsed_seconds": float64(10),
   152  				"resource":        wantResource,
   153  			},
   154  		},
   155  		{
   156  			"@level":   "info",
   157  			"@message": "test_instance.boop: Still creating... [20s elapsed]",
   158  			"@module":  "terraform.ui",
   159  			"type":     "apply_progress",
   160  			"hook": map[string]interface{}{
   161  				"action":          string("create"),
   162  				"elapsed_seconds": float64(20),
   163  				"resource":        wantResource,
   164  			},
   165  		},
   166  		{
   167  			"@level":   "info",
   168  			"@message": "test_instance.boop: Creation complete after 22s [id=test]",
   169  			"@module":  "terraform.ui",
   170  			"type":     "apply_complete",
   171  			"hook": map[string]interface{}{
   172  				"action":          string("create"),
   173  				"elapsed_seconds": float64(22),
   174  				"id_key":          "id",
   175  				"id_value":        "test",
   176  				"resource":        wantResource,
   177  			},
   178  		},
   179  	}
   180  
   181  	testJSONViewOutputEquals(t, done(t).Stdout(), want)
   182  }
   183  
   184  func TestJSONHook_errors(t *testing.T) {
   185  	streams, done := terminal.StreamsForTesting(t)
   186  	hook := newJSONHook(NewJSONView(NewView(streams)))
   187  
   188  	addr := addrs.Resource{
   189  		Mode: addrs.ManagedResourceMode,
   190  		Type: "test_instance",
   191  		Name: "boop",
   192  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
   193  	priorState := cty.NullVal(cty.Object(map[string]cty.Type{
   194  		"id":  cty.String,
   195  		"bar": cty.List(cty.String),
   196  	}))
   197  	plannedNewState := cty.ObjectVal(map[string]cty.Value{
   198  		"id": cty.StringVal("test"),
   199  		"bar": cty.ListVal([]cty.Value{
   200  			cty.StringVal("baz"),
   201  		}),
   202  	})
   203  
   204  	action, err := hook.PreApply(addr, states.CurrentGen, plans.Delete, priorState, plannedNewState)
   205  	testHookReturnValues(t, action, err)
   206  
   207  	provisionError := fmt.Errorf("provisioner didn't want to")
   208  	action, err = hook.PostProvisionInstanceStep(addr, "local-exec", provisionError)
   209  	testHookReturnValues(t, action, err)
   210  
   211  	applyError := fmt.Errorf("provider was sad")
   212  	action, err = hook.PostApply(addr, states.CurrentGen, plannedNewState, applyError)
   213  	testHookReturnValues(t, action, err)
   214  
   215  	// Shut down the progress goroutine
   216  	hook.applyingLock.Lock()
   217  	for key, progress := range hook.applying {
   218  		close(progress.done)
   219  		<-progress.heartbeatDone
   220  		delete(hook.applying, key)
   221  	}
   222  	hook.applyingLock.Unlock()
   223  
   224  	wantResource := map[string]interface{}{
   225  		"addr":             string("test_instance.boop"),
   226  		"implied_provider": string("test"),
   227  		"module":           string(""),
   228  		"resource":         string("test_instance.boop"),
   229  		"resource_key":     nil,
   230  		"resource_name":    string("boop"),
   231  		"resource_type":    string("test_instance"),
   232  	}
   233  	want := []map[string]interface{}{
   234  		{
   235  			"@level":   "info",
   236  			"@message": "test_instance.boop: Destroying...",
   237  			"@module":  "terraform.ui",
   238  			"type":     "apply_start",
   239  			"hook": map[string]interface{}{
   240  				"action":   string("delete"),
   241  				"resource": wantResource,
   242  			},
   243  		},
   244  		{
   245  			"@level":   "info",
   246  			"@message": "test_instance.boop: (local-exec) Provisioning errored",
   247  			"@module":  "terraform.ui",
   248  			"type":     "provision_errored",
   249  			"hook": map[string]interface{}{
   250  				"provisioner": "local-exec",
   251  				"resource":    wantResource,
   252  			},
   253  		},
   254  		{
   255  			"@level":   "info",
   256  			"@message": "test_instance.boop: Destruction errored after 0s",
   257  			"@module":  "terraform.ui",
   258  			"type":     "apply_errored",
   259  			"hook": map[string]interface{}{
   260  				"action":          string("delete"),
   261  				"elapsed_seconds": float64(0),
   262  				"resource":        wantResource,
   263  			},
   264  		},
   265  	}
   266  
   267  	testJSONViewOutputEquals(t, done(t).Stdout(), want)
   268  }
   269  
   270  func TestJSONHook_refresh(t *testing.T) {
   271  	streams, done := terminal.StreamsForTesting(t)
   272  	hook := newJSONHook(NewJSONView(NewView(streams)))
   273  
   274  	addr := addrs.Resource{
   275  		Mode: addrs.DataResourceMode,
   276  		Type: "test_data_source",
   277  		Name: "beep",
   278  	}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance)
   279  	state := cty.ObjectVal(map[string]cty.Value{
   280  		"id": cty.StringVal("honk"),
   281  		"bar": cty.ListVal([]cty.Value{
   282  			cty.StringVal("baz"),
   283  		}),
   284  	})
   285  
   286  	action, err := hook.PreRefresh(addr, states.CurrentGen, state)
   287  	testHookReturnValues(t, action, err)
   288  
   289  	action, err = hook.PostRefresh(addr, states.CurrentGen, state, state)
   290  	testHookReturnValues(t, action, err)
   291  
   292  	wantResource := map[string]interface{}{
   293  		"addr":             string("data.test_data_source.beep"),
   294  		"implied_provider": string("test"),
   295  		"module":           string(""),
   296  		"resource":         string("data.test_data_source.beep"),
   297  		"resource_key":     nil,
   298  		"resource_name":    string("beep"),
   299  		"resource_type":    string("test_data_source"),
   300  	}
   301  	want := []map[string]interface{}{
   302  		{
   303  			"@level":   "info",
   304  			"@message": "data.test_data_source.beep: Refreshing state... [id=honk]",
   305  			"@module":  "terraform.ui",
   306  			"type":     "refresh_start",
   307  			"hook": map[string]interface{}{
   308  				"resource": wantResource,
   309  				"id_key":   "id",
   310  				"id_value": "honk",
   311  			},
   312  		},
   313  		{
   314  			"@level":   "info",
   315  			"@message": "data.test_data_source.beep: Refresh complete [id=honk]",
   316  			"@module":  "terraform.ui",
   317  			"type":     "refresh_complete",
   318  			"hook": map[string]interface{}{
   319  				"resource": wantResource,
   320  				"id_key":   "id",
   321  				"id_value": "honk",
   322  			},
   323  		},
   324  	}
   325  
   326  	testJSONViewOutputEquals(t, done(t).Stdout(), want)
   327  }
   328  
   329  func testHookReturnValues(t *testing.T, action terraform.HookAction, err error) {
   330  	t.Helper()
   331  
   332  	if err != nil {
   333  		t.Fatal(err)
   334  	}
   335  	if action != terraform.HookActionContinue {
   336  		t.Fatalf("Expected hook to continue, given: %#v", action)
   337  	}
   338  }