github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/builtin/provisioners/local-exec/resource_provisioner_test.go (about) 1 package localexec 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/hashicorp/terraform/internal/provisioners" 12 "github.com/mitchellh/cli" 13 "github.com/zclconf/go-cty/cty" 14 ) 15 16 func TestResourceProvider_Apply(t *testing.T) { 17 defer os.Remove("test_out") 18 output := cli.NewMockUi() 19 p := New() 20 schema := p.GetSchema().Provisioner 21 c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 22 "command": cty.StringVal("echo foo > test_out"), 23 })) 24 if err != nil { 25 t.Fatal(err) 26 } 27 28 resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ 29 Config: c, 30 UIOutput: output, 31 }) 32 33 if resp.Diagnostics.HasErrors() { 34 t.Fatalf("err: %v", resp.Diagnostics.Err()) 35 } 36 37 // Check the file 38 raw, err := ioutil.ReadFile("test_out") 39 if err != nil { 40 t.Fatalf("err: %v", err) 41 } 42 43 actual := strings.TrimSpace(string(raw)) 44 expected := "foo" 45 if actual != expected { 46 t.Fatalf("bad: %#v", actual) 47 } 48 } 49 50 func TestResourceProvider_stop(t *testing.T) { 51 output := cli.NewMockUi() 52 p := New() 53 schema := p.GetSchema().Provisioner 54 55 c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 56 // bash/zsh/ksh will exec a single command in the same process. This 57 // makes certain there's a subprocess in the shell. 58 "command": cty.StringVal("sleep 30; sleep 30"), 59 })) 60 if err != nil { 61 t.Fatal(err) 62 } 63 64 doneCh := make(chan struct{}) 65 startTime := time.Now() 66 go func() { 67 defer close(doneCh) 68 // The functionality of p.Apply is tested in TestResourceProvider_Apply. 69 // Because p.Apply is called in a goroutine, trying to t.Fatal() on its 70 // result would be ignored or would cause a panic if the parent goroutine 71 // has already completed. 72 _ = p.ProvisionResource(provisioners.ProvisionResourceRequest{ 73 Config: c, 74 UIOutput: output, 75 }) 76 }() 77 78 mustExceed := (50 * time.Millisecond) 79 select { 80 case <-doneCh: 81 t.Fatalf("expected to finish sometime after %s finished in %s", mustExceed, time.Since(startTime)) 82 case <-time.After(mustExceed): 83 t.Logf("correctly took longer than %s", mustExceed) 84 } 85 86 // Stop it 87 stopTime := time.Now() 88 p.Stop() 89 90 maxTempl := "expected to finish under %s, finished in %s" 91 finishWithin := (2 * time.Second) 92 select { 93 case <-doneCh: 94 t.Logf(maxTempl, finishWithin, time.Since(stopTime)) 95 case <-time.After(finishWithin): 96 t.Fatalf(maxTempl, finishWithin, time.Since(stopTime)) 97 } 98 } 99 100 func TestResourceProvider_ApplyCustomInterpreter(t *testing.T) { 101 output := cli.NewMockUi() 102 p := New() 103 104 schema := p.GetSchema().Provisioner 105 106 c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 107 "interpreter": cty.ListVal([]cty.Value{cty.StringVal("echo"), cty.StringVal("is")}), 108 "command": cty.StringVal("not really an interpreter"), 109 })) 110 if err != nil { 111 t.Fatal(err) 112 } 113 114 resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ 115 Config: c, 116 UIOutput: output, 117 }) 118 119 if resp.Diagnostics.HasErrors() { 120 t.Fatal(resp.Diagnostics.Err()) 121 } 122 123 got := strings.TrimSpace(output.OutputWriter.String()) 124 want := `Executing: ["echo" "is" "not really an interpreter"] 125 is not really an interpreter` 126 if got != want { 127 t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) 128 } 129 } 130 131 func TestResourceProvider_ApplyCustomWorkingDirectory(t *testing.T) { 132 testdir := "working_dir_test" 133 os.Mkdir(testdir, 0755) 134 defer os.Remove(testdir) 135 136 output := cli.NewMockUi() 137 p := New() 138 schema := p.GetSchema().Provisioner 139 140 c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 141 "working_dir": cty.StringVal(testdir), 142 "command": cty.StringVal("echo `pwd`"), 143 })) 144 if err != nil { 145 t.Fatal(err) 146 } 147 148 resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ 149 Config: c, 150 UIOutput: output, 151 }) 152 153 if resp.Diagnostics.HasErrors() { 154 t.Fatal(resp.Diagnostics.Err()) 155 } 156 157 dir, err := os.Getwd() 158 if err != nil { 159 t.Fatalf("err: %v", err) 160 } 161 162 got := strings.TrimSpace(output.OutputWriter.String()) 163 want := "Executing: [\"/bin/sh\" \"-c\" \"echo `pwd`\"]\n" + dir + "/" + testdir 164 if got != want { 165 t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) 166 } 167 } 168 169 func TestResourceProvider_ApplyCustomEnv(t *testing.T) { 170 output := cli.NewMockUi() 171 p := New() 172 schema := p.GetSchema().Provisioner 173 174 c, err := schema.CoerceValue(cty.ObjectVal(map[string]cty.Value{ 175 "command": cty.StringVal("echo $FOO $BAR $BAZ"), 176 "environment": cty.MapVal(map[string]cty.Value{ 177 "FOO": cty.StringVal("BAR"), 178 "BAR": cty.StringVal("1"), 179 "BAZ": cty.StringVal("true"), 180 }), 181 })) 182 if err != nil { 183 t.Fatal(err) 184 } 185 186 resp := p.ProvisionResource(provisioners.ProvisionResourceRequest{ 187 Config: c, 188 UIOutput: output, 189 }) 190 if resp.Diagnostics.HasErrors() { 191 t.Fatal(resp.Diagnostics.Err()) 192 } 193 194 got := strings.TrimSpace(output.OutputWriter.String()) 195 want := `Executing: ["/bin/sh" "-c" "echo $FOO $BAR $BAZ"] 196 BAR 1 true` 197 if got != want { 198 t.Errorf("wrong output\ngot: %s\nwant: %s", got, want) 199 } 200 } 201 202 // Validate that Stop can Close can be called even when not provisioning. 203 func TestResourceProvisioner_StopClose(t *testing.T) { 204 p := New() 205 p.Stop() 206 p.Close() 207 } 208 209 func TestResourceProvisioner_nullsInOptionals(t *testing.T) { 210 output := cli.NewMockUi() 211 p := New() 212 schema := p.GetSchema().Provisioner 213 214 for i, cfg := range []cty.Value{ 215 cty.ObjectVal(map[string]cty.Value{ 216 "command": cty.StringVal("echo OK"), 217 "environment": cty.MapVal(map[string]cty.Value{ 218 "FOO": cty.NullVal(cty.String), 219 }), 220 }), 221 cty.ObjectVal(map[string]cty.Value{ 222 "command": cty.StringVal("echo OK"), 223 "environment": cty.NullVal(cty.Map(cty.String)), 224 }), 225 cty.ObjectVal(map[string]cty.Value{ 226 "command": cty.StringVal("echo OK"), 227 "interpreter": cty.ListVal([]cty.Value{cty.NullVal(cty.String)}), 228 }), 229 cty.ObjectVal(map[string]cty.Value{ 230 "command": cty.StringVal("echo OK"), 231 "interpreter": cty.NullVal(cty.List(cty.String)), 232 }), 233 cty.ObjectVal(map[string]cty.Value{ 234 "command": cty.StringVal("echo OK"), 235 "working_dir": cty.NullVal(cty.String), 236 }), 237 } { 238 t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { 239 240 cfg, err := schema.CoerceValue(cfg) 241 if err != nil { 242 t.Fatal(err) 243 } 244 245 // verifying there are no panics 246 p.ProvisionResource(provisioners.ProvisionResourceRequest{ 247 Config: cfg, 248 UIOutput: output, 249 }) 250 }) 251 } 252 }