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 }