github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/builtin/provisioners/remote-exec/resource_provisioner_test.go (about)

     1  package remoteexec
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"log"
     9  	"testing"
    10  	"time"
    11  
    12  	"strings"
    13  
    14  	"github.com/hashicorp/terraform/internal/communicator"
    15  	"github.com/hashicorp/terraform/internal/communicator/remote"
    16  	"github.com/hashicorp/terraform/internal/provisioners"
    17  	"github.com/mitchellh/cli"
    18  	"github.com/zclconf/go-cty/cty"
    19  )
    20  
    21  func TestResourceProvider_Validate_good(t *testing.T) {
    22  	c := cty.ObjectVal(map[string]cty.Value{
    23  		"inline": cty.ListVal([]cty.Value{cty.StringVal("echo foo")}),
    24  	})
    25  
    26  	resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{
    27  		Config: c,
    28  	})
    29  	if len(resp.Diagnostics) > 0 {
    30  		t.Fatal(resp.Diagnostics.ErrWithWarnings())
    31  	}
    32  }
    33  
    34  func TestResourceProvider_Validate_bad(t *testing.T) {
    35  	c := cty.ObjectVal(map[string]cty.Value{
    36  		"invalid": cty.StringVal("nope"),
    37  	})
    38  
    39  	resp := New().ValidateProvisionerConfig(provisioners.ValidateProvisionerConfigRequest{
    40  		Config: c,
    41  	})
    42  	if !resp.Diagnostics.HasErrors() {
    43  		t.Fatalf("Should have errors")
    44  	}
    45  }
    46  
    47  var expectedScriptOut = `cd /tmp
    48  wget http://foobar
    49  exit 0
    50  `
    51  
    52  func TestResourceProvider_generateScript(t *testing.T) {
    53  	inline := cty.ListVal([]cty.Value{
    54  		cty.StringVal("cd /tmp"),
    55  		cty.StringVal("wget http://foobar"),
    56  		cty.StringVal("exit 0"),
    57  	})
    58  
    59  	out, err := generateScripts(inline)
    60  	if err != nil {
    61  		t.Fatalf("err: %v", err)
    62  	}
    63  
    64  	if len(out) != 1 {
    65  		t.Fatal("expected 1 out")
    66  	}
    67  
    68  	if out[0] != expectedScriptOut {
    69  		t.Fatalf("bad: %v", out)
    70  	}
    71  }
    72  
    73  func TestResourceProvider_generateScriptEmptyInline(t *testing.T) {
    74  	inline := cty.ListVal([]cty.Value{cty.StringVal("")})
    75  
    76  	_, err := generateScripts(inline)
    77  	if err == nil {
    78  		t.Fatal("expected error, got none")
    79  	}
    80  
    81  	if !strings.Contains(err.Error(), "empty string") {
    82  		t.Fatalf("expected empty string error, got: %s", err)
    83  	}
    84  }
    85  
    86  func TestResourceProvider_CollectScripts_inline(t *testing.T) {
    87  	conf := map[string]cty.Value{
    88  		"inline": cty.ListVal([]cty.Value{
    89  			cty.StringVal("cd /tmp"),
    90  			cty.StringVal("wget http://foobar"),
    91  			cty.StringVal("exit 0"),
    92  		}),
    93  	}
    94  
    95  	scripts, err := collectScripts(cty.ObjectVal(conf))
    96  	if err != nil {
    97  		t.Fatalf("err: %v", err)
    98  	}
    99  
   100  	if len(scripts) != 1 {
   101  		t.Fatalf("bad: %v", scripts)
   102  	}
   103  
   104  	var out bytes.Buffer
   105  	_, err = io.Copy(&out, scripts[0])
   106  	if err != nil {
   107  		t.Fatalf("err: %v", err)
   108  	}
   109  
   110  	if out.String() != expectedScriptOut {
   111  		t.Fatalf("bad: %v", out.String())
   112  	}
   113  }
   114  
   115  func TestResourceProvider_CollectScripts_script(t *testing.T) {
   116  	p := New()
   117  	schema := p.GetSchema().Provisioner
   118  
   119  	conf, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
   120  		"scripts": cty.ListVal([]cty.Value{
   121  			cty.StringVal("testdata/script1.sh"),
   122  		}),
   123  	}))
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	scripts, err := collectScripts(conf)
   129  	if err != nil {
   130  		t.Fatalf("err: %v", err)
   131  	}
   132  
   133  	if len(scripts) != 1 {
   134  		t.Fatalf("bad: %v", scripts)
   135  	}
   136  
   137  	var out bytes.Buffer
   138  	_, err = io.Copy(&out, scripts[0])
   139  	if err != nil {
   140  		t.Fatalf("err: %v", err)
   141  	}
   142  
   143  	if out.String() != expectedScriptOut {
   144  		t.Fatalf("bad: %v", out.String())
   145  	}
   146  }
   147  
   148  func TestResourceProvider_CollectScripts_scripts(t *testing.T) {
   149  	p := New()
   150  	schema := p.GetSchema().Provisioner
   151  
   152  	conf, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
   153  		"scripts": cty.ListVal([]cty.Value{
   154  			cty.StringVal("testdata/script1.sh"),
   155  			cty.StringVal("testdata/script1.sh"),
   156  			cty.StringVal("testdata/script1.sh"),
   157  		}),
   158  	}))
   159  	if err != nil {
   160  		log.Fatal(err)
   161  	}
   162  
   163  	scripts, err := collectScripts(conf)
   164  	if err != nil {
   165  		t.Fatalf("err: %v", err)
   166  	}
   167  
   168  	if len(scripts) != 3 {
   169  		t.Fatalf("bad: %v", scripts)
   170  	}
   171  
   172  	for idx := range scripts {
   173  		var out bytes.Buffer
   174  		_, err = io.Copy(&out, scripts[idx])
   175  		if err != nil {
   176  			t.Fatalf("err: %v", err)
   177  		}
   178  
   179  		if out.String() != expectedScriptOut {
   180  			t.Fatalf("bad: %v", out.String())
   181  		}
   182  	}
   183  }
   184  
   185  func TestResourceProvider_CollectScripts_scriptsEmpty(t *testing.T) {
   186  	p := New()
   187  	schema := p.GetSchema().Provisioner
   188  
   189  	conf, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{
   190  		"scripts": cty.ListVal([]cty.Value{cty.StringVal("")}),
   191  	}))
   192  	if err != nil {
   193  		t.Fatal(err)
   194  	}
   195  
   196  	_, err = collectScripts(conf)
   197  	if err == nil {
   198  		t.Fatal("expected error")
   199  	}
   200  
   201  	if !strings.Contains(err.Error(), "empty string") {
   202  		t.Fatalf("Expected empty string error, got: %s", err)
   203  	}
   204  }
   205  
   206  func TestProvisionerTimeout(t *testing.T) {
   207  	o := cli.NewMockUi()
   208  	c := new(communicator.MockCommunicator)
   209  
   210  	disconnected := make(chan struct{})
   211  	c.DisconnectFunc = func() error {
   212  		close(disconnected)
   213  		return nil
   214  	}
   215  
   216  	completed := make(chan struct{})
   217  	c.CommandFunc = func(cmd *remote.Cmd) error {
   218  		defer close(completed)
   219  		cmd.Init()
   220  		time.Sleep(2 * time.Second)
   221  		cmd.SetExitStatus(0, nil)
   222  		return nil
   223  	}
   224  	c.ConnTimeout = time.Second
   225  	c.UploadScripts = map[string]string{"hello": "echo hello"}
   226  	c.RemoteScriptPath = "hello"
   227  
   228  	conf := map[string]cty.Value{
   229  		"inline": cty.ListVal([]cty.Value{cty.StringVal("echo hello")}),
   230  	}
   231  
   232  	scripts, err := collectScripts(cty.ObjectVal(conf))
   233  	if err != nil {
   234  		t.Fatal(err)
   235  	}
   236  
   237  	ctx := context.Background()
   238  
   239  	done := make(chan struct{})
   240  
   241  	var runErr error
   242  	go func() {
   243  		defer close(done)
   244  		runErr = runScripts(ctx, o, c, scripts)
   245  	}()
   246  
   247  	select {
   248  	case <-disconnected:
   249  		t.Fatal("communicator disconnected before command completed")
   250  	case <-completed:
   251  	}
   252  
   253  	<-done
   254  	if runErr != nil {
   255  		t.Fatal(err)
   256  	}
   257  }
   258  
   259  // Validate that Stop can Close can be called even when not provisioning.
   260  func TestResourceProvisioner_StopClose(t *testing.T) {
   261  	p := New()
   262  	p.Stop()
   263  	p.Close()
   264  }
   265  
   266  func TestResourceProvisioner_connectionRequired(t *testing.T) {
   267  	p := New()
   268  	resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{})
   269  	if !resp.Diagnostics.HasErrors() {
   270  		t.Fatal("expected error")
   271  	}
   272  
   273  	got := resp.Diagnostics.Err().Error()
   274  	if !strings.Contains(got, "Missing connection") {
   275  		t.Fatalf("expected 'Missing connection' error: got %q", got)
   276  	}
   277  }
   278  
   279  func TestResourceProvisioner_nullsInOptionals(t *testing.T) {
   280  	output := cli.NewMockUi()
   281  	p := New()
   282  	schema := p.GetSchema().Provisioner
   283  
   284  	for i, cfg := range []cty.Value{
   285  		cty.ObjectVal(map[string]cty.Value{
   286  			"script": cty.StringVal("echo"),
   287  			"inline": cty.NullVal(cty.List(cty.String)),
   288  		}),
   289  		cty.ObjectVal(map[string]cty.Value{
   290  			"inline": cty.ListVal([]cty.Value{
   291  				cty.NullVal(cty.String),
   292  			}),
   293  		}),
   294  		cty.ObjectVal(map[string]cty.Value{
   295  			"script": cty.NullVal(cty.String),
   296  		}),
   297  		cty.ObjectVal(map[string]cty.Value{
   298  			"scripts": cty.NullVal(cty.List(cty.String)),
   299  		}),
   300  		cty.ObjectVal(map[string]cty.Value{
   301  			"scripts": cty.ListVal([]cty.Value{
   302  				cty.NullVal(cty.String),
   303  			}),
   304  		}),
   305  	} {
   306  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
   307  
   308  			cfg, err := schema.CoerceValue(cfg)
   309  			if err != nil {
   310  				t.Fatal(err)
   311  			}
   312  
   313  			// verifying there are no panics
   314  			p.ProvisionResource(provisioners.ProvisionResourceRequest{
   315  				Config:   cfg,
   316  				UIOutput: output,
   317  			})
   318  		})
   319  	}
   320  }