github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/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  )
    23  
    24  const MagicCookieKey = "PACKER_PLUGIN_MAGIC_COOKIE"
    25  const MagicCookieValue = "d602bf8f470bc67ca7faa0386276bbdd4330efaf76d1a219cb4d6991ca9872b2"
    26  
    27  // This serves a single RPC connection on the given RPC server on
    28  // a random port.
    29  func serve(server *rpc.Server) (err error) {
    30  	if os.Getenv(MagicCookieKey) != MagicCookieValue {
    31  		return errors.New("Please do not execute plugins directly. Packer will execute these for you.")
    32  	}
    33  
    34  	// If there is no explicit number of Go threads to use, then set it
    35  	if os.Getenv("GOMAXPROCS") == "" {
    36  		runtime.GOMAXPROCS(runtime.NumCPU())
    37  	}
    38  
    39  	minPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MIN_PORT"), 10, 32)
    40  	if err != nil {
    41  		return
    42  	}
    43  
    44  	maxPort, err := strconv.ParseInt(os.Getenv("PACKER_PLUGIN_MAX_PORT"), 10, 32)
    45  	if err != nil {
    46  		return
    47  	}
    48  
    49  	log.Printf("Plugin minimum port: %d\n", minPort)
    50  	log.Printf("Plugin maximum port: %d\n", maxPort)
    51  
    52  	// Set the RPC port range
    53  	packrpc.PortRange(int(minPort), int(maxPort))
    54  
    55  	var address string
    56  	var listener net.Listener
    57  	for port := minPort; port <= maxPort; port++ {
    58  		address = fmt.Sprintf("127.0.0.1:%d", port)
    59  		listener, err = net.Listen("tcp", address)
    60  		if err != nil {
    61  			err = nil
    62  			continue
    63  		}
    64  
    65  		break
    66  	}
    67  
    68  	defer listener.Close()
    69  
    70  	// Output the address to stdout
    71  	log.Printf("Plugin address: %s\n", address)
    72  	fmt.Println(address)
    73  	os.Stdout.Sync()
    74  
    75  	// Accept a connection
    76  	log.Println("Waiting for connection...")
    77  	conn, err := listener.Accept()
    78  	if err != nil {
    79  		log.Printf("Error accepting connection: %s\n", err.Error())
    80  		return
    81  	}
    82  
    83  	// Serve a single connection
    84  	log.Println("Serving a plugin connection...")
    85  	server.ServeConn(conn)
    86  	return
    87  }
    88  
    89  // Registers a signal handler to "swallow" interrupts so that the
    90  // plugin isn't killed. The main host Packer process is responsible
    91  // for killing the plugins when interrupted.
    92  func swallowInterrupts() {
    93  	ch := make(chan os.Signal, 1)
    94  	signal.Notify(ch, os.Interrupt)
    95  
    96  	go func() {
    97  		<-ch
    98  		log.Println("Received interrupt signal. Ignoring.")
    99  	}()
   100  }
   101  
   102  // Serves a builder from a plugin.
   103  func ServeBuilder(builder packer.Builder) {
   104  	log.Println("Preparing to serve a builder plugin...")
   105  
   106  	server := rpc.NewServer()
   107  	packrpc.RegisterBuilder(server, builder)
   108  
   109  	swallowInterrupts()
   110  	if err := serve(server); err != nil {
   111  		log.Printf("ERROR: %s", err)
   112  		os.Exit(1)
   113  	}
   114  }
   115  
   116  // Serves a command from a plugin.
   117  func ServeCommand(command packer.Command) {
   118  	log.Println("Preparing to serve a command plugin...")
   119  
   120  	server := rpc.NewServer()
   121  	packrpc.RegisterCommand(server, command)
   122  
   123  	swallowInterrupts()
   124  	if err := serve(server); err != nil {
   125  		log.Printf("ERROR: %s", err)
   126  		os.Exit(1)
   127  	}
   128  }
   129  
   130  // Serves a hook from a plugin.
   131  func ServeHook(hook packer.Hook) {
   132  	log.Println("Preparing to serve a hook plugin...")
   133  
   134  	server := rpc.NewServer()
   135  	packrpc.RegisterHook(server, hook)
   136  
   137  	swallowInterrupts()
   138  	if err := serve(server); err != nil {
   139  		log.Printf("ERROR: %s", err)
   140  		os.Exit(1)
   141  	}
   142  }
   143  
   144  // Serves a post-processor from a plugin.
   145  func ServePostProcessor(p packer.PostProcessor) {
   146  	log.Println("Preparing to serve a post-processor plugin...")
   147  
   148  	server := rpc.NewServer()
   149  	packrpc.RegisterPostProcessor(server, p)
   150  
   151  	swallowInterrupts()
   152  	if err := serve(server); err != nil {
   153  		log.Printf("ERROR: %s", err)
   154  		os.Exit(1)
   155  	}
   156  }
   157  
   158  // Serves a provisioner from a plugin.
   159  func ServeProvisioner(p packer.Provisioner) {
   160  	log.Println("Preparing to serve a provisioner plugin...")
   161  
   162  	server := rpc.NewServer()
   163  	packrpc.RegisterProvisioner(server, p)
   164  
   165  	swallowInterrupts()
   166  	if err := serve(server); err != nil {
   167  		log.Printf("ERROR: %s", err)
   168  		os.Exit(1)
   169  	}
   170  }