github.com/jerryclinesmith/packer@v0.3.7/packer/plugin/plugin.go (about)

     1  // The plugin package provides the functionality to both expose a Packer
     2  // plugin binary and to connect to an existing Packer plugin binary.
     3  //
     4  // Packer supports plugins in the form of self-contained external static
     5  // Go binaries. These binaries behave in a certain way (enforced by this
     6  // package) and are connected to in a certain way (also enforced by this
     7  // package).
     8  package plugin
     9  
    10  import (
    11  	"errors"
    12  	"fmt"
    13  	"github.com/mitchellh/packer/packer"
    14  	packrpc "github.com/mitchellh/packer/packer/rpc"
    15  	"log"
    16  	"net"
    17  	"net/rpc"
    18  	"os"
    19  	"os/signal"
    20  	"runtime"
    21  	"strconv"
    22  	"sync/atomic"
    23  )
    24  
    25  // This is a count of the number of interrupts the process has received.
    26  // This is updated with sync/atomic whenever a SIGINT is received and can
    27  // be checked by the plugin safely to take action.
    28  var Interrupts int32 = 0
    29  
    30  const MagicCookieKey = "PACKER_PLUGIN_MAGIC_COOKIE"
    31  const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2"
    32  
    33  // This serves a single RPC connection on the given RPC server on
    34  // a random port.
    35  func serve(server *rpc.Server) (err error) {
    36  	if os.Getenv(MagicCookieKey) != MagicCookieValue {
    37  		return errors.New("Please do not execute plugins directly. Packer will execute these for you.")
    38  	}
    39  
    40  	// If there is no explicit number of Go threads to use, then set it
    41  	if os.Getenv("GOMAXPROCS") == "" {
    42  		runtime.GOMAXPROCS(runtime.NumCPU())
    43  	}
    44  
    45  	minPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MIN_PORT"), 10, 32)
    46  	if err != nil {
    47  		return
    48  	}
    49  
    50  	maxPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MAX_PORT"), 10, 32)
    51  	if err != nil {
    52  		return
    53  	}
    54  
    55  	log.Printf("Plugin minimum port: %d\n", minPort)
    56  	log.Printf("Plugin maximum port: %d\n", maxPort)
    57  
    58  	// Set the RPC port range
    59  	packrpc.PortRange(int(minPort), int(maxPort))
    60  
    61  	var address string
    62  	var listener net.Listener
    63  	for port := minPort; port <= maxPort; port++ {
    64  		address = fmt.Sprintf("127.0.0.1:%d", port)
    65  		listener, err = net.Listen("tcp", address)
    66  		if err != nil {
    67  			err = nil
    68  			continue
    69  		}
    70  
    71  		break
    72  	}
    73  
    74  	defer listener.Close()
    75  
    76  	// Output the address to stdout
    77  	log.Printf("Plugin address: %s\n", address)
    78  	fmt.Println(address)
    79  	os.Stdout.Sync()
    80  
    81  	// Accept a connection
    82  	log.Println("Waiting for connection...")
    83  	conn, err := listener.Accept()
    84  	if err != nil {
    85  		log.Printf("Error accepting connection: %s\n", err.Error())
    86  		return
    87  	}
    88  
    89  	// Serve a single connection
    90  	log.Println("Serving a plugin connection...")
    91  	server.ServeConn(conn)
    92  	return
    93  }
    94  
    95  // Registers a signal handler to swallow and count interrupts so that the
    96  // plugin isn't killed. The main host Packer process is responsible
    97  // for killing the plugins when interrupted.
    98  func countInterrupts() {
    99  	ch := make(chan os.Signal, 1)
   100  	signal.Notify(ch, os.Interrupt)
   101  
   102  	go func() {
   103  		for {
   104  			<-ch
   105  			newCount := atomic.AddInt32(&Interrupts, 1)
   106  			log.Printf("Received interrupt signal (count: %d). Ignoring.", newCount)
   107  		}
   108  	}()
   109  }
   110  
   111  // Serves a builder from a plugin.
   112  func ServeBuilder(builder packer.Builder) {
   113  	log.Println("Preparing to serve a builder plugin...")
   114  
   115  	server := rpc.NewServer()
   116  	packrpc.RegisterBuilder(server, builder)
   117  
   118  	countInterrupts()
   119  	if err := serve(server); err != nil {
   120  		log.Printf("ERROR: %s", err)
   121  		os.Exit(1)
   122  	}
   123  }
   124  
   125  // Serves a command from a plugin.
   126  func ServeCommand(command packer.Command) {
   127  	log.Println("Preparing to serve a command plugin...")
   128  
   129  	server := rpc.NewServer()
   130  	packrpc.RegisterCommand(server, command)
   131  
   132  	countInterrupts()
   133  	if err := serve(server); err != nil {
   134  		log.Printf("ERROR: %s", err)
   135  		os.Exit(1)
   136  	}
   137  }
   138  
   139  // Serves a hook from a plugin.
   140  func ServeHook(hook packer.Hook) {
   141  	log.Println("Preparing to serve a hook plugin...")
   142  
   143  	server := rpc.NewServer()
   144  	packrpc.RegisterHook(server, hook)
   145  
   146  	countInterrupts()
   147  	if err := serve(server); err != nil {
   148  		log.Printf("ERROR: %s", err)
   149  		os.Exit(1)
   150  	}
   151  }
   152  
   153  // Serves a post-processor from a plugin.
   154  func ServePostProcessor(p packer.PostProcessor) {
   155  	log.Println("Preparing to serve a post-processor plugin...")
   156  
   157  	server := rpc.NewServer()
   158  	packrpc.RegisterPostProcessor(server, p)
   159  
   160  	countInterrupts()
   161  	if err := serve(server); err != nil {
   162  		log.Printf("ERROR: %s", err)
   163  		os.Exit(1)
   164  	}
   165  }
   166  
   167  // Serves a provisioner from a plugin.
   168  func ServeProvisioner(p packer.Provisioner) {
   169  	log.Println("Preparing to serve a provisioner plugin...")
   170  
   171  	server := rpc.NewServer()
   172  	packrpc.RegisterProvisioner(server, p)
   173  
   174  	countInterrupts()
   175  	if err := serve(server); err != nil {
   176  		log.Printf("ERROR: %s", err)
   177  		os.Exit(1)
   178  	}
   179  }
   180  
   181  // Tests whether or not the plugin was interrupted or not.
   182  func Interrupted() bool {
   183  	return atomic.LoadInt32(&Interrupts) > 0
   184  }