github.com/hashicorp/terraform-plugin-sdk@v1.17.2/plugin/debug.go (about)

     1  package plugin
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"os"
     9  	"os/signal"
    10  	"time"
    11  
    12  	"github.com/hashicorp/go-plugin"
    13  )
    14  
    15  // ReattachConfig holds the information Terraform needs to be able to attach
    16  // itself to a provider process, so it can drive the process.
    17  type ReattachConfig struct {
    18  	Protocol string
    19  	Pid      int
    20  	Test     bool
    21  	Addr     ReattachConfigAddr
    22  }
    23  
    24  // ReattachConfigAddr is a JSON-encoding friendly version of net.Addr.
    25  type ReattachConfigAddr struct {
    26  	Network string
    27  	String  string
    28  }
    29  
    30  // DebugServe starts a plugin server in debug mode; this should only be used
    31  // when the provider will manage its own lifecycle. It is not recommended for
    32  // normal usage; Serve is the correct function for that.
    33  func DebugServe(ctx context.Context, opts *ServeOpts) (ReattachConfig, <-chan struct{}, error) {
    34  	reattachCh := make(chan *plugin.ReattachConfig)
    35  	closeCh := make(chan struct{})
    36  
    37  	opts.TestConfig = &plugin.ServeTestConfig{
    38  		Context:          ctx,
    39  		ReattachConfigCh: reattachCh,
    40  		CloseCh:          closeCh,
    41  	}
    42  
    43  	go Serve(opts)
    44  
    45  	var config *plugin.ReattachConfig
    46  	select {
    47  	case config = <-reattachCh:
    48  	case <-time.After(2 * time.Second):
    49  		return ReattachConfig{}, closeCh, errors.New("timeout waiting on reattach config")
    50  	}
    51  
    52  	if config == nil {
    53  		return ReattachConfig{}, closeCh, errors.New("nil reattach config received")
    54  	}
    55  
    56  	return ReattachConfig{
    57  		Protocol: string(config.Protocol),
    58  		Pid:      config.Pid,
    59  		Test:     config.Test,
    60  		Addr: ReattachConfigAddr{
    61  			Network: config.Addr.Network(),
    62  			String:  config.Addr.String(),
    63  		},
    64  	}, closeCh, nil
    65  }
    66  
    67  // Debug starts a debug server and controls its lifecycle, printing the
    68  // information needed for Terraform to connect to the provider to stdout.
    69  // os.Interrupt will be captured and used to stop the server.
    70  func Debug(ctx context.Context, providerAddr string, opts *ServeOpts) error {
    71  	ctx, cancel := context.WithCancel(ctx)
    72  	// Ctrl-C will stop the server
    73  	sigCh := make(chan os.Signal, 1)
    74  	signal.Notify(sigCh, os.Interrupt)
    75  	defer func() {
    76  		signal.Stop(sigCh)
    77  		cancel()
    78  	}()
    79  	config, closeCh, err := DebugServe(ctx, opts)
    80  	if err != nil {
    81  		return fmt.Errorf("Error launching debug server: %v", err)
    82  	}
    83  	go func() {
    84  		select {
    85  		case <-sigCh:
    86  			cancel()
    87  		case <-ctx.Done():
    88  		}
    89  	}()
    90  	reattachStr, err := json.Marshal(map[string]ReattachConfig{
    91  		providerAddr: config,
    92  	})
    93  	if err != nil {
    94  		return fmt.Errorf("Error building reattach string: %v", err)
    95  	}
    96  
    97  	fmt.Printf("Provider server started; to attach Terraform, set TF_REATTACH_PROVIDERS to the following:\n%s\n", string(reattachStr))
    98  
    99  	// wait for the server to be done
   100  	<-closeCh
   101  	return nil
   102  }