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