github.com/phobos182/packer@v0.2.3-0.20130819023704-c84d2aeffc68/packer/rpc/builder.go (about)

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