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