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  }