github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-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/muratcelep/terraform/not-internal/e2e"
    15  	"github.com/muratcelep/terraform/not-internal/grpcwrap"
    16  	tfplugin5 "github.com/muratcelep/terraform/not-internal/plugin"
    17  	tfplugin "github.com/muratcelep/terraform/not-internal/plugin6"
    18  	simple5 "github.com/muratcelep/terraform/not-internal/provider-simple"
    19  	simple "github.com/muratcelep/terraform/not-internal/provider-simple-v6"
    20  	proto5 "github.com/muratcelep/terraform/not-internal/tfplugin5"
    21  	proto "github.com/muratcelep/terraform/not-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(terraformBin, fixturePath)
   150  	defer tf.Close()
   151  
   152  	reattachCh := make(chan *plugin.ReattachConfig)
   153  	closeCh := make(chan struct{})
   154  	provider := &providerServer{
   155  		ProviderServer: grpcwrap.Provider6(simple.Provider()),
   156  	}
   157  	ctx, cancel := context.WithCancel(context.Background())
   158  	defer cancel()
   159  	go plugin.Serve(&plugin.ServeConfig{
   160  		Logger: hclog.New(&hclog.LoggerOptions{
   161  			Name:   "plugintest",
   162  			Level:  hclog.Trace,
   163  			Output: ioutil.Discard,
   164  		}),
   165  		Test: &plugin.ServeTestConfig{
   166  			Context:          ctx,
   167  			ReattachConfigCh: reattachCh,
   168  			CloseCh:          closeCh,
   169  		},
   170  		GRPCServer: plugin.DefaultGRPCServer,
   171  		VersionedPlugins: map[int]plugin.PluginSet{
   172  			6: {
   173  				"provider": &tfplugin.GRPCProviderPlugin{
   174  					GRPCProvider: func() proto.ProviderServer {
   175  						return provider
   176  					},
   177  				},
   178  			},
   179  		},
   180  	})
   181  	config := <-reattachCh
   182  	if config == nil {
   183  		t.Fatalf("no reattach config received")
   184  	}
   185  	reattachStr, err := json.Marshal(map[string]reattachConfig{
   186  		"hashicorp/test": {
   187  			Protocol:        string(config.Protocol),
   188  			ProtocolVersion: 6,
   189  			Pid:             config.Pid,
   190  			Test:            true,
   191  			Addr: reattachConfigAddr{
   192  				Network: config.Addr.Network(),
   193  				String:  config.Addr.String(),
   194  			},
   195  		},
   196  	})
   197  	if err != nil {
   198  		t.Fatal(err)
   199  	}
   200  
   201  	tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr))
   202  
   203  	//// INIT
   204  	stdout, stderr, err := tf.Run("init")
   205  	if err != nil {
   206  		t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
   207  	}
   208  
   209  	// Make sure we didn't download the binary
   210  	if strings.Contains(stdout, "Installing hashicorp/test v") {
   211  		t.Errorf("test provider download message is present in init output:\n%s", stdout)
   212  	}
   213  	if tf.FileExists(filepath.Join(".terraform", "plugins", "registry.terraform.io", "hashicorp", "test")) {
   214  		t.Errorf("test provider binary found in .terraform dir")
   215  	}
   216  
   217  	//// PLAN
   218  	_, stderr, err = tf.Run("plan", "-out=tfplan")
   219  	if err != nil {
   220  		t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
   221  	}
   222  
   223  	if !provider.PlanResourceChangeCalled() {
   224  		t.Error("PlanResourceChange not called on un-managed provider")
   225  	}
   226  
   227  	//// APPLY
   228  	_, stderr, err = tf.Run("apply", "tfplan")
   229  	if err != nil {
   230  		t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
   231  	}
   232  
   233  	if !provider.ApplyResourceChangeCalled() {
   234  		t.Error("ApplyResourceChange not called on un-managed provider")
   235  	}
   236  	provider.ResetApplyResourceChangeCalled()
   237  
   238  	//// DESTROY
   239  	_, stderr, err = tf.Run("destroy", "-auto-approve")
   240  	if err != nil {
   241  		t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
   242  	}
   243  
   244  	if !provider.ApplyResourceChangeCalled() {
   245  		t.Error("ApplyResourceChange (destroy) not called on in-process provider")
   246  	}
   247  	cancel()
   248  	<-closeCh
   249  }
   250  
   251  func TestUnmanagedSeparatePlan_proto5(t *testing.T) {
   252  	t.Parallel()
   253  
   254  	fixturePath := filepath.Join("testdata", "test-provider")
   255  	tf := e2e.NewBinary(terraformBin, fixturePath)
   256  	defer tf.Close()
   257  
   258  	reattachCh := make(chan *plugin.ReattachConfig)
   259  	closeCh := make(chan struct{})
   260  	provider := &providerServer5{
   261  		ProviderServer: grpcwrap.Provider(simple5.Provider()),
   262  	}
   263  	ctx, cancel := context.WithCancel(context.Background())
   264  	defer cancel()
   265  	go plugin.Serve(&plugin.ServeConfig{
   266  		Logger: hclog.New(&hclog.LoggerOptions{
   267  			Name:   "plugintest",
   268  			Level:  hclog.Trace,
   269  			Output: ioutil.Discard,
   270  		}),
   271  		Test: &plugin.ServeTestConfig{
   272  			Context:          ctx,
   273  			ReattachConfigCh: reattachCh,
   274  			CloseCh:          closeCh,
   275  		},
   276  		GRPCServer: plugin.DefaultGRPCServer,
   277  		VersionedPlugins: map[int]plugin.PluginSet{
   278  			5: {
   279  				"provider": &tfplugin5.GRPCProviderPlugin{
   280  					GRPCProvider: func() proto5.ProviderServer {
   281  						return provider
   282  					},
   283  				},
   284  			},
   285  		},
   286  	})
   287  	config := <-reattachCh
   288  	if config == nil {
   289  		t.Fatalf("no reattach config received")
   290  	}
   291  	reattachStr, err := json.Marshal(map[string]reattachConfig{
   292  		"hashicorp/test": {
   293  			Protocol:        string(config.Protocol),
   294  			ProtocolVersion: 5,
   295  			Pid:             config.Pid,
   296  			Test:            true,
   297  			Addr: reattachConfigAddr{
   298  				Network: config.Addr.Network(),
   299  				String:  config.Addr.String(),
   300  			},
   301  		},
   302  	})
   303  	if err != nil {
   304  		t.Fatal(err)
   305  	}
   306  
   307  	tf.AddEnv("TF_REATTACH_PROVIDERS=" + string(reattachStr))
   308  
   309  	//// INIT
   310  	stdout, stderr, err := tf.Run("init")
   311  	if err != nil {
   312  		t.Fatalf("unexpected init error: %s\nstderr:\n%s", err, stderr)
   313  	}
   314  
   315  	// Make sure we didn't download the binary
   316  	if strings.Contains(stdout, "Installing hashicorp/test v") {
   317  		t.Errorf("test provider download message is present in init output:\n%s", stdout)
   318  	}
   319  	if tf.FileExists(filepath.Join(".terraform", "plugins", "registry.terraform.io", "hashicorp", "test")) {
   320  		t.Errorf("test provider binary found in .terraform dir")
   321  	}
   322  
   323  	//// PLAN
   324  	_, stderr, err = tf.Run("plan", "-out=tfplan")
   325  	if err != nil {
   326  		t.Fatalf("unexpected plan error: %s\nstderr:\n%s", err, stderr)
   327  	}
   328  
   329  	if !provider.PlanResourceChangeCalled() {
   330  		t.Error("PlanResourceChange not called on un-managed provider")
   331  	}
   332  
   333  	//// APPLY
   334  	_, stderr, err = tf.Run("apply", "tfplan")
   335  	if err != nil {
   336  		t.Fatalf("unexpected apply error: %s\nstderr:\n%s", err, stderr)
   337  	}
   338  
   339  	if !provider.ApplyResourceChangeCalled() {
   340  		t.Error("ApplyResourceChange not called on un-managed provider")
   341  	}
   342  	provider.ResetApplyResourceChangeCalled()
   343  
   344  	//// DESTROY
   345  	_, stderr, err = tf.Run("destroy", "-auto-approve")
   346  	if err != nil {
   347  		t.Fatalf("unexpected destroy error: %s\nstderr:\n%s", err, stderr)
   348  	}
   349  
   350  	if !provider.ApplyResourceChangeCalled() {
   351  		t.Error("ApplyResourceChange (destroy) not called on in-process provider")
   352  	}
   353  	cancel()
   354  	<-closeCh
   355  }