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 }