github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/e2etest/unmanaged_test.go (about)

     1  package e2etest
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io/ioutil"
     7  	"path/filepath"
     8  	"strings"
     9  	"sync"
    10  	"testing"
    11  
    12  	"github.com/hashicorp/go-hclog"
    13  	"github.com/hashicorp/go-plugin"
    14  	"github.com/hashicorp/terraform/internal/e2e"
    15  	"github.com/hashicorp/terraform/internal/grpcwrap"
    16  	tfplugin5 "github.com/hashicorp/terraform/internal/plugin"
    17  	tfplugin "github.com/hashicorp/terraform/internal/plugin6"
    18  	simple5 "github.com/hashicorp/terraform/internal/provider-simple"
    19  	simple "github.com/hashicorp/terraform/internal/provider-simple-v6"
    20  	proto5 "github.com/hashicorp/terraform/internal/tfplugin5"
    21  	proto "github.com/hashicorp/terraform/internal/tfplugin6"
    22  )
    23  
    24  // The tests in this file are for the "unmanaged provider workflow", which
    25  // includes variants of the following sequence, with different details:
    26  // terraform init
    27  // terraform plan
    28  // terraform apply
    29  //
    30  // These tests are run against an in-process server, and checked to make sure
    31  // they're not trying to control the lifecycle of the binary. They are not
    32  // checked for correctness of the operations themselves.
    33  
    34  type reattachConfig struct {
    35  	Protocol        string
    36  	ProtocolVersion int
    37  	Pid             int
    38  	Test            bool
    39  	Addr            reattachConfigAddr
    40  }
    41  
    42  type reattachConfigAddr struct {
    43  	Network string
    44  	String  string
    45  }
    46  
    47  type providerServer struct {
    48  	sync.Mutex
    49  	proto.ProviderServer
    50  	planResourceChangeCalled  bool
    51  	applyResourceChangeCalled bool
    52  }
    53  
    54  func (p *providerServer) PlanResourceChange(ctx context.Context, req *proto.PlanResourceChange_Request) (*proto.PlanResourceChange_Response, error) {
    55  	p.Lock()
    56  	defer p.Unlock()
    57  
    58  	p.planResourceChangeCalled = true
    59  	return p.ProviderServer.PlanResourceChange(ctx, req)
    60  }
    61  
    62  func (p *providerServer) ApplyResourceChange(ctx context.Context, req *proto.ApplyResourceChange_Request) (*proto.ApplyResourceChange_Response, error) {
    63  	p.Lock()
    64  	defer p.Unlock()
    65  
    66  	p.applyResourceChangeCalled = true
    67  	return p.ProviderServer.ApplyResourceChange(ctx, req)
    68  }
    69  
    70  func (p *providerServer) PlanResourceChangeCalled() bool {
    71  	p.Lock()
    72  	defer p.Unlock()
    73  
    74  	return p.planResourceChangeCalled
    75  }
    76  func (p *providerServer) ResetPlanResourceChangeCalled() {
    77  	p.Lock()
    78  	defer p.Unlock()
    79  
    80  	p.planResourceChangeCalled = false
    81  }
    82  
    83  func (p *providerServer) ApplyResourceChangeCalled() bool {
    84  	p.Lock()
    85  	defer p.Unlock()
    86  
    87  	return p.applyResourceChangeCalled
    88  }
    89  func (p *providerServer) ResetApplyResourceChangeCalled() {
    90  	p.Lock()
    91  	defer p.Unlock()
    92  
    93  	p.applyResourceChangeCalled = false
    94  }
    95  
    96  type providerServer5 struct {
    97  	sync.Mutex
    98  	proto5.ProviderServer
    99  	planResourceChangeCalled  bool
   100  	applyResourceChangeCalled bool
   101  }
   102  
   103  func (p *providerServer5) PlanResourceChange(ctx context.Context, req *proto5.PlanResourceChange_Request) (*proto5.PlanResourceChange_Response, error) {
   104  	p.Lock()
   105  	defer p.Unlock()
   106  
   107  	p.planResourceChangeCalled = true
   108  	return p.ProviderServer.PlanResourceChange(ctx, req)
   109  }
   110  
   111  func (p *providerServer5) ApplyResourceChange(ctx context.Context, req *proto5.ApplyResourceChange_Request) (*proto5.ApplyResourceChange_Response, error) {
   112  	p.Lock()
   113  	defer p.Unlock()
   114  
   115  	p.applyResourceChangeCalled = true
   116  	return p.ProviderServer.ApplyResourceChange(ctx, req)
   117  }
   118  
   119  func (p *providerServer5) PlanResourceChangeCalled() bool {
   120  	p.Lock()
   121  	defer p.Unlock()
   122  
   123  	return p.planResourceChangeCalled
   124  }
   125  func (p *providerServer5) ResetPlanResourceChangeCalled() {
   126  	p.Lock()
   127  	defer p.Unlock()
   128  
   129  	p.planResourceChangeCalled = false
   130  }
   131  
   132  func (p *providerServer5) ApplyResourceChangeCalled() bool {
   133  	p.Lock()
   134  	defer p.Unlock()
   135  
   136  	return p.applyResourceChangeCalled
   137  }
   138  func (p *providerServer5) ResetApplyResourceChangeCalled() {
   139  	p.Lock()
   140  	defer p.Unlock()
   141  
   142  	p.applyResourceChangeCalled = false
   143  }
   144  
   145  func TestUnmanagedSeparatePlan(t *testing.T) {
   146  	t.Parallel()
   147  
   148  	fixturePath := filepath.Join("testdata", "test-provider")
   149  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   150  
   151  	reattachCh := make(chan *plugin.ReattachConfig)
   152  	closeCh := make(chan struct{})
   153  	provider := &providerServer{
   154  		ProviderServer: grpcwrap.Provider6(simple.Provider()),
   155  	}
   156  	ctx, cancel := context.WithCancel(context.Background())
   157  	defer cancel()
   158  	go plugin.Serve(&plugin.ServeConfig{
   159  		Logger: hclog.New(&hclog.LoggerOptions{
   160  			Name:   "plugintest",
   161  			Level:  hclog.Trace,
   162  			Output: ioutil.Discard,
   163  		}),
   164  		Test: &plugin.ServeTestConfig{
   165  			Context:          ctx,
   166  			ReattachConfigCh: reattachCh,
   167  			CloseCh:          closeCh,
   168  		},
   169  		GRPCServer: plugin.DefaultGRPCServer,
   170  		VersionedPlugins: map[int]plugin.PluginSet{
   171  			6: {
   172  				"provider": &tfplugin.GRPCProviderPlugin{
   173  					GRPCProvider: func() proto.ProviderServer {
   174  						return provider
   175  					},
   176  				},
   177  			},
   178  		},
   179  	})
   180  	config := <-reattachCh
   181  	if config == nil {
   182  		t.Fatalf("no reattach config received")
   183  	}
   184  	reattachStr, err := json.Marshal(map[string]reattachConfig{
   185  		"hashicorp/test": {
   186  			Protocol:        string(config.Protocol),
   187  			ProtocolVersion: 6,
   188  			Pid:             config.Pid,
   189  			Test:            true,
   190  			Addr: reattachConfigAddr{
   191  				Network: config.Addr.Network(),
   192  				String:  config.Addr.String(),
   193  			},
   194  		},
   195  	})
   196  	if err != nil {
   197  		t.Fatal(err)
   198  	}
   199  
   200  	tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr))
   201  
   202  	//// INIT
   203  	stdout, stderr, err := tf.Run("init")
   204  	if err != nil {
   205  		t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
   206  	}
   207  
   208  	// Make sure we didn't download the binary
   209  	if strings.Contains(stdout, "Installing hashicorp/test v") {
   210  		t.Errorf("test provider download message is present in init output:\n%s", stdout)
   211  	}
   212  	if tf.FileExists(filepath.Join(".terraform", "plugins", "registry.terraform.io", "hashicorp", "test")) {
   213  		t.Errorf("test provider binary found in .terraform dir")
   214  	}
   215  
   216  	//// PLAN
   217  	_, stderr, err = tf.Run("plan", "-out=tfplan")
   218  	if err != nil {
   219  		t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
   220  	}
   221  
   222  	if !provider.PlanResourceChangeCalled() {
   223  		t.Error("PlanResourceChange not called on un-managed provider")
   224  	}
   225  
   226  	//// APPLY
   227  	_, stderr, err = tf.Run("apply", "tfplan")
   228  	if err != nil {
   229  		t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
   230  	}
   231  
   232  	if !provider.ApplyResourceChangeCalled() {
   233  		t.Error("ApplyResourceChange not called on un-managed provider")
   234  	}
   235  	provider.ResetApplyResourceChangeCalled()
   236  
   237  	//// DESTROY
   238  	_, stderr, err = tf.Run("destroy", "-auto-approve")
   239  	if err != nil {
   240  		t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
   241  	}
   242  
   243  	if !provider.ApplyResourceChangeCalled() {
   244  		t.Error("ApplyResourceChange (destroy) not called on in-process provider")
   245  	}
   246  	cancel()
   247  	<-closeCh
   248  }
   249  
   250  func TestUnmanagedSeparatePlan_proto5(t *testing.T) {
   251  	t.Parallel()
   252  
   253  	fixturePath := filepath.Join("testdata", "test-provider")
   254  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   255  
   256  	reattachCh := make(chan *plugin.ReattachConfig)
   257  	closeCh := make(chan struct{})
   258  	provider := &providerServer5{
   259  		ProviderServer: grpcwrap.Provider(simple5.Provider()),
   260  	}
   261  	ctx, cancel := context.WithCancel(context.Background())
   262  	defer cancel()
   263  	go plugin.Serve(&plugin.ServeConfig{
   264  		Logger: hclog.New(&hclog.LoggerOptions{
   265  			Name:   "plugintest",
   266  			Level:  hclog.Trace,
   267  			Output: ioutil.Discard,
   268  		}),
   269  		Test: &plugin.ServeTestConfig{
   270  			Context:          ctx,
   271  			ReattachConfigCh: reattachCh,
   272  			CloseCh:          closeCh,
   273  		},
   274  		GRPCServer: plugin.DefaultGRPCServer,
   275  		VersionedPlugins: map[int]plugin.PluginSet{
   276  			5: {
   277  				"provider": &tfplugin5.GRPCProviderPlugin{
   278  					GRPCProvider: func() proto5.ProviderServer {
   279  						return provider
   280  					},
   281  				},
   282  			},
   283  		},
   284  	})
   285  	config := <-reattachCh
   286  	if config == nil {
   287  		t.Fatalf("no reattach config received")
   288  	}
   289  	reattachStr, err := json.Marshal(map[string]reattachConfig{
   290  		"hashicorp/test": {
   291  			Protocol:        string(config.Protocol),
   292  			ProtocolVersion: 5,
   293  			Pid:             config.Pid,
   294  			Test:            true,
   295  			Addr: reattachConfigAddr{
   296  				Network: config.Addr.Network(),
   297  				String:  config.Addr.String(),
   298  			},
   299  		},
   300  	})
   301  	if err != nil {
   302  		t.Fatal(err)
   303  	}
   304  
   305  	tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr))
   306  
   307  	//// INIT
   308  	stdout, stderr, err := tf.Run("init")
   309  	if err != nil {
   310  		t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
   311  	}
   312  
   313  	// Make sure we didn't download the binary
   314  	if strings.Contains(stdout, "Installing hashicorp/test v") {
   315  		t.Errorf("test provider download message is present in init output:\n%s", stdout)
   316  	}
   317  	if tf.FileExists(filepath.Join(".terraform", "plugins", "registry.terraform.io", "hashicorp", "test")) {
   318  		t.Errorf("test provider binary found in .terraform dir")
   319  	}
   320  
   321  	//// PLAN
   322  	_, stderr, err = tf.Run("plan", "-out=tfplan")
   323  	if err != nil {
   324  		t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
   325  	}
   326  
   327  	if !provider.PlanResourceChangeCalled() {
   328  		t.Error("PlanResourceChange not called on un-managed provider")
   329  	}
   330  
   331  	//// APPLY
   332  	_, stderr, err = tf.Run("apply", "tfplan")
   333  	if err != nil {
   334  		t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
   335  	}
   336  
   337  	if !provider.ApplyResourceChangeCalled() {
   338  		t.Error("ApplyResourceChange not called on un-managed provider")
   339  	}
   340  	provider.ResetApplyResourceChangeCalled()
   341  
   342  	//// DESTROY
   343  	_, stderr, err = tf.Run("destroy", "-auto-approve")
   344  	if err != nil {
   345  		t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
   346  	}
   347  
   348  	if !provider.ApplyResourceChangeCalled() {
   349  		t.Error("ApplyResourceChange (destroy) not called on in-process provider")
   350  	}
   351  	cancel()
   352  	<-closeCh
   353  }