Files
coder/provisionersdk/serve.go
Kyle Carberry 7c260f88d1 feat: Create provisioner abstraction (#12)
* feat: Create provisioner abstraction

Creates a provisioner abstraction that takes prior art from the Terraform plugin system. It's safe to assume this code will change a lot when it becomes integrated with provisionerd.

Closes #10.

* Ignore generated files in diff view

* Check for unstaged file changes

* Install protoc-gen-go

* Use proper drpc plugin version

* Fix serve closed pipe

* Install sqlc with curl for speed

* Fix install command

* Format CI action

* Add linguist-generated and closed pipe test

* Cleanup code from comments

* Add dRPC comment

* Add Terraform installer for cross-platform

* Build provisioner tests on Linux only
2022-01-08 11:24:02 -06:00

59 lines
1.6 KiB
Go

package provisionersdk
import (
"context"
"errors"
"io"
"golang.org/x/xerrors"
"storj.io/drpc"
"storj.io/drpc/drpcmux"
"storj.io/drpc/drpcserver"
"github.com/coder/coder/provisionersdk/proto"
)
// ServeOptions are configurations to serve a provisioner.
type ServeOptions struct {
// Transport specifies a custom transport to serve the dRPC connection.
Transport drpc.Transport
}
// Serve starts a dRPC connection for the provisioner and transport provided.
func Serve(ctx context.Context, server proto.DRPCProvisionerServer, options *ServeOptions) error {
if options == nil {
options = &ServeOptions{}
}
// Default to using stdio.
if options.Transport == nil {
options.Transport = TransportStdio()
}
// dRPC is a drop-in replacement for gRPC with less generated code, and faster transports.
// See: https://www.storj.io/blog/introducing-drpc-our-replacement-for-grpc
mux := drpcmux.New()
err := proto.DRPCRegisterProvisioner(mux, server)
if err != nil {
return xerrors.Errorf("register provisioner: %w", err)
}
srv := drpcserver.New(mux)
// Only serve a single connection on the transport.
// Transports are not multiplexed, and provisioners are
// short-lived processes that can be executed concurrently.
err = srv.ServeOne(ctx, options.Transport)
if err != nil {
if errors.Is(err, context.Canceled) {
return nil
}
if errors.Is(err, io.ErrClosedPipe) {
// This may occur if the transport on either end is
// closed before the context. It's fine to return
// nil here, since the server has nothing to
// communicate with.
return nil
}
return xerrors.Errorf("serve transport: %w", err)
}
return nil
}