Files
coder/peerbroker/dial.go
Kyle Carberry 82dfd6c72f feat: Add UI for awaiting agent connections (#578)
* feat: Add stage to build logs

This adds a stage property to logs, and refactors the job logs
cliui.

It also adds tests to the cliui for build logs!

* feat: Add stage to build logs

This adds a stage property to logs, and refactors the job logs
cliui.

It also adds tests to the cliui for build logs!

* feat: Add config-ssh and tests for resiliency

* Rename "Echo" test to "ImmediateExit"

* Fix Terraform resource agent association

* Fix logs post-cancel

* Fix select on Windows

* Remove terraform init logs

* Move timer into it's own loop

* Fix race condition in provisioner jobs

* Fix requested changes
2022-03-28 19:19:28 -05:00

88 lines
2.5 KiB
Go

package peerbroker
import (
"context"
"errors"
"io"
"reflect"
"github.com/pion/webrtc/v3"
"golang.org/x/xerrors"
"github.com/coder/coder/peer"
"github.com/coder/coder/peerbroker/proto"
)
// Dial consumes the PeerBroker gRPC connection negotiation stream to produce a WebRTC peered connection.
func Dial(stream proto.DRPCPeerBroker_NegotiateConnectionClient, iceServers []webrtc.ICEServer, opts *peer.ConnOptions) (*peer.Conn, error) {
peerConn, err := peer.Client(iceServers, opts)
if err != nil {
return nil, xerrors.Errorf("create peer connection: %w", err)
}
go func() {
defer stream.Close()
// Exchanging messages from the peer connection to negotiate a connection.
for {
select {
case <-peerConn.Closed():
return
case sessionDescription := <-peerConn.LocalSessionDescription():
err = stream.Send(&proto.Exchange{
Message: &proto.Exchange_Sdp{
Sdp: &proto.WebRTCSessionDescription{
SdpType: int32(sessionDescription.Type),
Sdp: sessionDescription.SDP,
},
},
})
if err != nil {
_ = peerConn.CloseWithError(xerrors.Errorf("send local session description: %w", err))
return
}
case iceCandidate := <-peerConn.LocalCandidate():
err = stream.Send(&proto.Exchange{
Message: &proto.Exchange_IceCandidate{
IceCandidate: iceCandidate.Candidate,
},
})
if err != nil {
_ = peerConn.CloseWithError(xerrors.Errorf("send local candidate: %w", err))
return
}
}
}
}()
go func() {
// Exchanging messages from the server to negotiate a connection.
for {
serverToClientMessage, err := stream.Recv()
if err != nil {
// p2p connections should never die if this stream does due
// to proper closure or context cancellation!
if errors.Is(err, io.EOF) || errors.Is(err, context.Canceled) {
return
}
_ = peerConn.CloseWithError(xerrors.Errorf("recv: %w", err))
return
}
switch {
case serverToClientMessage.GetSdp() != nil:
peerConn.SetRemoteSessionDescription(webrtc.SessionDescription{
Type: webrtc.SDPType(serverToClientMessage.GetSdp().SdpType),
SDP: serverToClientMessage.GetSdp().Sdp,
})
case serverToClientMessage.GetIceCandidate() != "":
peerConn.AddRemoteCandidate(webrtc.ICECandidateInit{
Candidate: serverToClientMessage.GetIceCandidate(),
})
default:
_ = peerConn.CloseWithError(xerrors.Errorf("unhandled message: %s", reflect.TypeOf(serverToClientMessage).String()))
return
}
}
}()
return peerConn, nil
}