package rpc import ( "io" "log" "net/rpc" packersdk "github.com/hashicorp/packer/packer-plugin-sdk/packer" "github.com/ugorji/go/codec" ) const ( DefaultArtifactEndpoint string = "Artifact" DefaultBuildEndpoint = "Build" DefaultBuilderEndpoint = "Builder" DefaultCacheEndpoint = "Cache" DefaultCommandEndpoint = "Command" DefaultCommunicatorEndpoint = "Communicator" DefaultHookEndpoint = "Hook" DefaultPostProcessorEndpoint = "PostProcessor" DefaultProvisionerEndpoint = "Provisioner" DefaultUiEndpoint = "Ui" ) // Server represents an RPC server for Packer. This must be paired on // the other side with a Client. In Packer, each "plugin" (builder, provisioner, // and post-processor) creates and launches a server. The client created and // used by the packer "core" type Server struct { mux *muxBroker streamId uint32 server *rpc.Server closeMux bool } // NewServer returns a new Packer RPC server. func NewServer(conn io.ReadWriteCloser) (*Server, error) { mux, err := newMuxBrokerServer(conn) if err != nil { return nil, err } result := newServerWithMux(mux, 0) result.closeMux = true go mux.Run() return result, nil } func newServerWithMux(mux *muxBroker, streamId uint32) *Server { return &Server{ mux: mux, streamId: streamId, server: rpc.NewServer(), closeMux: false, } } func (s *Server) Close() error { if s.closeMux { log.Printf("[WARN] Shutting down mux conn in Server") return s.mux.Close() } return nil } func (s *Server) RegisterArtifact(a packersdk.Artifact) error { return s.server.RegisterName(DefaultArtifactEndpoint, &ArtifactServer{ artifact: a, }) } func (s *Server) RegisterBuild(b packersdk.Build) error { return s.server.RegisterName(DefaultBuildEndpoint, &BuildServer{ build: b, mux: s.mux, }) } func (s *Server) RegisterBuilder(b packersdk.Builder) error { return s.server.RegisterName(DefaultBuilderEndpoint, &BuilderServer{ commonServer: commonServer{ selfConfigurable: b, mux: s.mux, }, builder: b, }) } func (s *Server) RegisterCommunicator(c packersdk.Communicator) error { return s.server.RegisterName(DefaultCommunicatorEndpoint, &CommunicatorServer{ c: c, commonServer: commonServer{ mux: s.mux, }, }) } func (s *Server) RegisterHook(h packersdk.Hook) error { return s.server.RegisterName(DefaultHookEndpoint, &HookServer{ hook: h, mux: s.mux, }) } func (s *Server) RegisterPostProcessor(p packersdk.PostProcessor) error { return s.server.RegisterName(DefaultPostProcessorEndpoint, &PostProcessorServer{ commonServer: commonServer{ selfConfigurable: p, mux: s.mux, }, p: p, }) } func (s *Server) RegisterProvisioner(p packersdk.Provisioner) error { return s.server.RegisterName(DefaultProvisionerEndpoint, &ProvisionerServer{ commonServer: commonServer{ selfConfigurable: p, mux: s.mux, }, p: p, }) } func (s *Server) RegisterUi(ui packersdk.Ui) error { return s.server.RegisterName(DefaultUiEndpoint, &UiServer{ ui: ui, register: s.server.RegisterName, }) } // ServeConn serves a single connection over the RPC server. It is up // to the caller to obtain a proper io.ReadWriteCloser. func (s *Server) Serve() { // Accept a connection on stream ID 0, which is always used for // normal client to server connections. stream, err := s.mux.Accept(s.streamId) if err != nil { log.Printf("[ERR] Error retrieving stream for serving: %s", err) return } defer stream.Close() h := &codec.MsgpackHandle{ RawToString: true, WriteExt: true, } rpcCodec := codec.GoRpc.ServerCodec(stream, h) s.server.ServeCodec(rpcCodec) }