github.com/jerryclinesmith/packer@v0.3.7/packer/rpc/builder.go (about) 1 package rpc 2 3 import ( 4 "encoding/gob" 5 "fmt" 6 "github.com/mitchellh/packer/packer" 7 "log" 8 "net" 9 "net/rpc" 10 ) 11 12 // An implementation of packer.Builder where the builder is actually executed 13 // over an RPC connection. 14 type builder struct { 15 client *rpc.Client 16 } 17 18 // BuilderServer wraps a packer.Builder implementation and makes it exportable 19 // as part of a Golang RPC server. 20 type BuilderServer struct { 21 builder packer.Builder 22 } 23 24 type BuilderPrepareArgs struct { 25 Configs []interface{} 26 } 27 28 type BuilderRunArgs struct { 29 RPCAddress string 30 ResponseAddress string 31 } 32 33 type BuilderRunResponse struct { 34 Err error 35 RPCAddress string 36 } 37 38 func Builder(client *rpc.Client) *builder { 39 return &builder{client} 40 } 41 42 func (b *builder) Prepare(config ...interface{}) (err error) { 43 cerr := b.client.Call("Builder.Prepare", &BuilderPrepareArgs{config}, &err) 44 if cerr != nil { 45 err = cerr 46 } 47 48 return 49 } 50 51 func (b *builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { 52 // Create and start the server for the Build and UI 53 server := rpc.NewServer() 54 RegisterCache(server, cache) 55 RegisterHook(server, hook) 56 RegisterUi(server, ui) 57 58 // Create a server for the response 59 responseL := netListenerInRange(portRangeMin, portRangeMax) 60 runResponseCh := make(chan *BuilderRunResponse) 61 go func() { 62 defer responseL.Close() 63 64 var response BuilderRunResponse 65 defer func() { runResponseCh <- &response }() 66 67 conn, err := responseL.Accept() 68 if err != nil { 69 response.Err = err 70 return 71 } 72 defer conn.Close() 73 74 decoder := gob.NewDecoder(conn) 75 if err := decoder.Decode(&response); err != nil { 76 response.Err = fmt.Errorf("Error waiting for Run: %s", err) 77 } 78 }() 79 80 args := &BuilderRunArgs{ 81 serveSingleConn(server), 82 responseL.Addr().String(), 83 } 84 85 if err := b.client.Call("Builder.Run", args, new(interface{})); err != nil { 86 return nil, err 87 } 88 89 response := <-runResponseCh 90 if response.Err != nil { 91 return nil, response.Err 92 } 93 94 if response.RPCAddress == "" { 95 return nil, nil 96 } 97 98 client, err := rpc.Dial("tcp", response.RPCAddress) 99 if err != nil { 100 return nil, err 101 } 102 103 return Artifact(client), nil 104 } 105 106 func (b *builder) Cancel() { 107 if err := b.client.Call("Builder.Cancel", new(interface{}), new(interface{})); err != nil { 108 log.Printf("Error cancelling builder: %s", err) 109 } 110 } 111 112 func (b *BuilderServer) Prepare(args *BuilderPrepareArgs, reply *error) error { 113 err := b.builder.Prepare(args.Configs...) 114 if err != nil { 115 *reply = NewBasicError(err) 116 } 117 118 return nil 119 } 120 121 func (b *BuilderServer) Run(args *BuilderRunArgs, reply *interface{}) error { 122 client, err := rpc.Dial("tcp", args.RPCAddress) 123 if err != nil { 124 return err 125 } 126 127 responseC, err := net.Dial("tcp", args.ResponseAddress) 128 if err != nil { 129 return err 130 } 131 132 responseWriter := gob.NewEncoder(responseC) 133 134 // Run the build in a goroutine so we don't block the RPC connection 135 go func() { 136 defer responseC.Close() 137 138 cache := Cache(client) 139 hook := Hook(client) 140 ui := &Ui{client} 141 artifact, responseErr := b.builder.Run(ui, hook, cache) 142 responseAddress := "" 143 144 if responseErr == nil && artifact != nil { 145 // Wrap the artifact 146 server := rpc.NewServer() 147 RegisterArtifact(server, artifact) 148 responseAddress = serveSingleConn(server) 149 } 150 151 if responseErr != nil { 152 responseErr = NewBasicError(responseErr) 153 } 154 155 err := responseWriter.Encode(&BuilderRunResponse{responseErr, responseAddress}) 156 if err != nil { 157 log.Printf("BuildServer.Run error: %s", err) 158 } 159 }() 160 161 return nil 162 } 163 164 func (b *BuilderServer) Cancel(args *interface{}, reply *interface{}) error { 165 b.builder.Cancel() 166 return nil 167 }