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 }