github.com/Hashicorp/terraform@v0.11.12-beta1/helper/schema/provisioner_test.go (about) 1 package schema 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "testing" 8 "time" 9 10 "github.com/hashicorp/terraform/config" 11 "github.com/hashicorp/terraform/terraform" 12 ) 13 14 func TestProvisioner_impl(t *testing.T) { 15 var _ terraform.ResourceProvisioner = new(Provisioner) 16 } 17 18 func noopApply(ctx context.Context) error { 19 return nil 20 } 21 22 func TestProvisionerValidate(t *testing.T) { 23 cases := []struct { 24 Name string 25 P *Provisioner 26 Config map[string]interface{} 27 Err bool 28 Warns []string 29 }{ 30 { 31 Name: "No ApplyFunc", 32 P: &Provisioner{}, 33 Config: nil, 34 Err: true, 35 }, 36 { 37 Name: "Incorrect schema", 38 P: &Provisioner{ 39 Schema: map[string]*Schema{ 40 "foo": {}, 41 }, 42 ApplyFunc: noopApply, 43 }, 44 Config: nil, 45 Err: true, 46 }, 47 { 48 "Basic required field", 49 &Provisioner{ 50 Schema: map[string]*Schema{ 51 "foo": &Schema{ 52 Required: true, 53 Type: TypeString, 54 }, 55 }, 56 ApplyFunc: noopApply, 57 }, 58 nil, 59 true, 60 nil, 61 }, 62 63 { 64 "Basic required field set", 65 &Provisioner{ 66 Schema: map[string]*Schema{ 67 "foo": &Schema{ 68 Required: true, 69 Type: TypeString, 70 }, 71 }, 72 ApplyFunc: noopApply, 73 }, 74 map[string]interface{}{ 75 "foo": "bar", 76 }, 77 false, 78 nil, 79 }, 80 { 81 Name: "Warning from property validation", 82 P: &Provisioner{ 83 Schema: map[string]*Schema{ 84 "foo": { 85 Type: TypeString, 86 Optional: true, 87 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 88 ws = append(ws, "Simple warning from property validation") 89 return 90 }, 91 }, 92 }, 93 ApplyFunc: noopApply, 94 }, 95 Config: map[string]interface{}{ 96 "foo": "", 97 }, 98 Err: false, 99 Warns: []string{"Simple warning from property validation"}, 100 }, 101 { 102 Name: "No schema", 103 P: &Provisioner{ 104 Schema: nil, 105 ApplyFunc: noopApply, 106 }, 107 Config: nil, 108 Err: false, 109 }, 110 { 111 Name: "Warning from provisioner ValidateFunc", 112 P: &Provisioner{ 113 Schema: nil, 114 ApplyFunc: noopApply, 115 ValidateFunc: func(*terraform.ResourceConfig) (ws []string, errors []error) { 116 ws = append(ws, "Simple warning from provisioner ValidateFunc") 117 return 118 }, 119 }, 120 Config: nil, 121 Err: false, 122 Warns: []string{"Simple warning from provisioner ValidateFunc"}, 123 }, 124 } 125 126 for i, tc := range cases { 127 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 128 c, err := config.NewRawConfig(tc.Config) 129 if err != nil { 130 t.Fatalf("err: %s", err) 131 } 132 133 ws, es := tc.P.Validate(terraform.NewResourceConfig(c)) 134 if len(es) > 0 != tc.Err { 135 t.Fatalf("%d: %#v %s", i, es, es) 136 } 137 if (tc.Warns != nil || len(ws) != 0) && !reflect.DeepEqual(ws, tc.Warns) { 138 t.Fatalf("%d: warnings mismatch, actual: %#v", i, ws) 139 } 140 }) 141 } 142 } 143 144 func TestProvisionerApply(t *testing.T) { 145 cases := []struct { 146 Name string 147 P *Provisioner 148 Conn map[string]string 149 Config map[string]interface{} 150 Err bool 151 }{ 152 { 153 "Basic config", 154 &Provisioner{ 155 ConnSchema: map[string]*Schema{ 156 "foo": &Schema{ 157 Type: TypeString, 158 Optional: true, 159 }, 160 }, 161 162 Schema: map[string]*Schema{ 163 "foo": &Schema{ 164 Type: TypeInt, 165 Optional: true, 166 }, 167 }, 168 169 ApplyFunc: func(ctx context.Context) error { 170 cd := ctx.Value(ProvConnDataKey).(*ResourceData) 171 d := ctx.Value(ProvConfigDataKey).(*ResourceData) 172 if d.Get("foo").(int) != 42 { 173 return fmt.Errorf("bad config data") 174 } 175 if cd.Get("foo").(string) != "bar" { 176 return fmt.Errorf("bad conn data") 177 } 178 179 return nil 180 }, 181 }, 182 map[string]string{ 183 "foo": "bar", 184 }, 185 map[string]interface{}{ 186 "foo": 42, 187 }, 188 false, 189 }, 190 } 191 192 for i, tc := range cases { 193 t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) { 194 c, err := config.NewRawConfig(tc.Config) 195 if err != nil { 196 t.Fatalf("err: %s", err) 197 } 198 199 state := &terraform.InstanceState{ 200 Ephemeral: terraform.EphemeralState{ 201 ConnInfo: tc.Conn, 202 }, 203 } 204 205 err = tc.P.Apply( 206 nil, state, terraform.NewResourceConfig(c)) 207 if err != nil != tc.Err { 208 t.Fatalf("%d: %s", i, err) 209 } 210 }) 211 } 212 } 213 214 func TestProvisionerApply_nilState(t *testing.T) { 215 p := &Provisioner{ 216 ConnSchema: map[string]*Schema{ 217 "foo": &Schema{ 218 Type: TypeString, 219 Optional: true, 220 }, 221 }, 222 223 Schema: map[string]*Schema{ 224 "foo": &Schema{ 225 Type: TypeInt, 226 Optional: true, 227 }, 228 }, 229 230 ApplyFunc: func(ctx context.Context) error { 231 return nil 232 }, 233 } 234 235 conf := map[string]interface{}{ 236 "foo": 42, 237 } 238 239 c, err := config.NewRawConfig(conf) 240 if err != nil { 241 t.Fatalf("err: %s", err) 242 } 243 244 err = p.Apply(nil, nil, terraform.NewResourceConfig(c)) 245 if err != nil { 246 t.Fatalf("err: %s", err) 247 } 248 } 249 250 func TestProvisionerStop(t *testing.T) { 251 var p Provisioner 252 253 // Verify stopch blocks 254 ch := p.StopContext().Done() 255 select { 256 case <-ch: 257 t.Fatal("should not be stopped") 258 case <-time.After(10 * time.Millisecond): 259 } 260 261 // Stop it 262 if err := p.Stop(); err != nil { 263 t.Fatalf("err: %s", err) 264 } 265 266 select { 267 case <-ch: 268 case <-time.After(10 * time.Millisecond): 269 t.Fatal("should be stopped") 270 } 271 } 272 273 func TestProvisionerStop_apply(t *testing.T) { 274 p := &Provisioner{ 275 ConnSchema: map[string]*Schema{ 276 "foo": &Schema{ 277 Type: TypeString, 278 Optional: true, 279 }, 280 }, 281 282 Schema: map[string]*Schema{ 283 "foo": &Schema{ 284 Type: TypeInt, 285 Optional: true, 286 }, 287 }, 288 289 ApplyFunc: func(ctx context.Context) error { 290 <-ctx.Done() 291 return nil 292 }, 293 } 294 295 conn := map[string]string{ 296 "foo": "bar", 297 } 298 299 conf := map[string]interface{}{ 300 "foo": 42, 301 } 302 303 c, err := config.NewRawConfig(conf) 304 if err != nil { 305 t.Fatalf("err: %s", err) 306 } 307 308 state := &terraform.InstanceState{ 309 Ephemeral: terraform.EphemeralState{ 310 ConnInfo: conn, 311 }, 312 } 313 314 // Run the apply in a goroutine 315 doneCh := make(chan struct{}) 316 go func() { 317 p.Apply(nil, state, terraform.NewResourceConfig(c)) 318 close(doneCh) 319 }() 320 321 // Should block 322 select { 323 case <-doneCh: 324 t.Fatal("should not be done") 325 case <-time.After(10 * time.Millisecond): 326 } 327 328 // Stop! 329 p.Stop() 330 331 select { 332 case <-doneCh: 333 case <-time.After(10 * time.Millisecond): 334 t.Fatal("should be done") 335 } 336 } 337 338 func TestProvisionerStop_stopFirst(t *testing.T) { 339 var p Provisioner 340 341 // Stop it 342 if err := p.Stop(); err != nil { 343 t.Fatalf("err: %s", err) 344 } 345 346 select { 347 case <-p.StopContext().Done(): 348 case <-time.After(10 * time.Millisecond): 349 t.Fatal("should be stopped") 350 } 351 }