From 659bae0228ead472070dc5e6fff64f4b24ea045a Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Wed, 26 Jun 2024 14:05:07 +0000 Subject: [PATCH 1/6] chore: add DRPC server implementation for network telemetry --- coderd/agentapi/api.go | 40 +- coderd/coderd.go | 24 +- coderd/telemetry/telemetry.go | 305 +++++++++++ coderd/workspaceagentsrpc.go | 24 +- tailnet/proto/tailnet.pb.go | 979 ++++++++++++++++++---------------- tailnet/proto/tailnet.proto | 37 +- tailnet/service.go | 95 +++- 7 files changed, 1001 insertions(+), 503 deletions(-) diff --git a/coderd/agentapi/api.go b/coderd/agentapi/api.go index 4e5e30ad9c761..37c84a966f4cb 100644 --- a/coderd/agentapi/api.go +++ b/coderd/agentapi/api.go @@ -23,6 +23,7 @@ import ( "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/prometheusmetrics" "github.com/coder/coder/v2/coderd/schedule" + "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/coderd/workspacestats" "github.com/coder/coder/v2/codersdk" @@ -47,6 +48,7 @@ type API struct { mu sync.Mutex cachedWorkspaceID uuid.UUID + drpcServiceClose func() } var _ agentproto.DRPCAgentServer = &API{} @@ -65,15 +67,18 @@ type Options struct { AppearanceFetcher *atomic.Pointer[appearance.Fetcher] PublishWorkspaceUpdateFn func(ctx context.Context, workspaceID uuid.UUID) PublishWorkspaceAgentLogsUpdateFn func(ctx context.Context, workspaceAgentID uuid.UUID, msg agentsdk.LogsNotifyMessage) - - AccessURL *url.URL - AppHostname string - AgentStatsRefreshInterval time.Duration - DisableDirectConnections bool - DerpForceWebSockets bool - DerpMapUpdateFrequency time.Duration - ExternalAuthConfigs []*externalauth.Config - Experiments codersdk.Experiments + NetworkTelemetryBatchFn func(batch []telemetry.NetworkEvent) + + AccessURL *url.URL + AppHostname string + AgentStatsRefreshInterval time.Duration + DisableDirectConnections bool + DerpForceWebSockets bool + DerpMapUpdateFrequency time.Duration + NetworkTelemetryBatchFrequency time.Duration + NetworkTelemetryBatchMaxSize int + ExternalAuthConfigs []*externalauth.Config + Experiments codersdk.Experiments // Optional: // WorkspaceID avoids a future lookup to find the workspace ID by setting @@ -154,15 +159,24 @@ func New(opts Options) *API { } api.DRPCService = &tailnet.DRPCService{ - CoordPtr: opts.TailnetCoordinator, - Logger: opts.Log, - DerpMapUpdateFrequency: opts.DerpMapUpdateFrequency, - DerpMapFn: opts.DerpMapFn, + CoordPtr: opts.TailnetCoordinator, + Logger: opts.Log, + DerpMapUpdateFrequency: opts.DerpMapUpdateFrequency, + DerpMapFn: opts.DerpMapFn, + NetworkTelemetryBatchFrequency: opts.NetworkTelemetryBatchFrequency, + NetworkTelemetryBatchMaxSize: opts.NetworkTelemetryBatchMaxSize, + NetworkTelemetryBatchFn: opts.NetworkTelemetryBatchFn, } + api.drpcServiceClose = api.DRPCService.PeriodicTelemetryBatcher() return api } +func (a *API) Close() error { + a.drpcServiceClose() + return nil +} + func (a *API) Server(ctx context.Context) (*drpcserver.Server, error) { mux := drpcmux.New() err := agentproto.DRPCRegisterAgent(mux, a) diff --git a/coderd/coderd.go b/coderd/coderd.go index 288eca9a4dbaf..292b0cb171574 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -142,14 +142,16 @@ type Options struct { DERPServer *derp.Server // BaseDERPMap is used as the base DERP map for all clients and agents. // Proxies are added to this list. - BaseDERPMap *tailcfg.DERPMap - DERPMapUpdateFrequency time.Duration - SwaggerEndpoint bool - SetUserGroups func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, orgGroupNames map[uuid.UUID][]string, createMissingGroups bool) error - SetUserSiteRoles func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, roles []string) error - TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore] - UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore] - AccessControlStore *atomic.Pointer[dbauthz.AccessControlStore] + BaseDERPMap *tailcfg.DERPMap + DERPMapUpdateFrequency time.Duration + NetworkTelemetryBatchFrequency time.Duration + NetworkTelemetryBatchMaxSize int + SwaggerEndpoint bool + SetUserGroups func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, orgGroupNames map[uuid.UUID][]string, createMissingGroups bool) error + SetUserSiteRoles func(ctx context.Context, logger slog.Logger, tx database.Store, userID uuid.UUID, roles []string) error + TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore] + UserQuietHoursScheduleStore *atomic.Pointer[schedule.UserQuietHoursScheduleStore] + AccessControlStore *atomic.Pointer[dbauthz.AccessControlStore] // AppSecurityKey is the crypto key used to sign and encrypt tokens related to // workspace applications. It consists of both a signing and encryption key. AppSecurityKey workspaceapps.SecurityKey @@ -305,6 +307,12 @@ func New(options *Options) *API { if options.DERPMapUpdateFrequency == 0 { options.DERPMapUpdateFrequency = 5 * time.Second } + if options.NetworkTelemetryBatchFrequency == 0 { + options.NetworkTelemetryBatchFrequency = 1 * time.Minute + } + if options.NetworkTelemetryBatchMaxSize == 0 { + options.NetworkTelemetryBatchMaxSize = 1_000 + } if options.TailnetCoordinator == nil { options.TailnetCoordinator = tailnet.NewCoordinator(options.Logger) } diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index 91251053663f5..d2dfbaba44204 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -20,6 +20,8 @@ import ( "github.com/google/uuid" "golang.org/x/sync/errgroup" "golang.org/x/xerrors" + "google.golang.org/protobuf/types/known/durationpb" + "google.golang.org/protobuf/types/known/wrapperspb" "cdr.dev/slog" "github.com/coder/coder/v2/buildinfo" @@ -27,6 +29,7 @@ import ( "github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/codersdk" + tailnetproto "github.com/coder/coder/v2/tailnet/proto" ) const ( @@ -795,6 +798,7 @@ type Snapshot struct { WorkspaceResourceMetadata []WorkspaceResourceMetadata `json:"workspace_resource_metadata"` WorkspaceResources []WorkspaceResource `json:"workspace_resources"` Workspaces []Workspace `json:"workspaces"` + NetworkEvents []NetworkEvent `json:"network_events"` } // Deployment contains information about the host running Coder. @@ -1006,6 +1010,307 @@ type ExternalProvisioner struct { ShutdownAt *time.Time `json:"shutdown_at"` } +type NetworkEventIPFields struct { + Version int32 `json:"version"` // 4 or 6 + Class string `json:"class"` // public, private, link_local, unique_local, loopback +} + +func ipFieldsFromProto(proto *tailnetproto.IPFields) NetworkEventIPFields { + if proto == nil { + return NetworkEventIPFields{} + } + return NetworkEventIPFields{ + Version: proto.Version, + Class: strings.ToLower(proto.Class.String()), + } +} + +type NetworkEventP2PEndpoint struct { + Hash string `json:"hash"` + Port int `json:"port"` + Fields NetworkEventIPFields `json:"fields"` +} + +func p2pEndpointFromProto(proto *tailnetproto.TelemetryEvent_P2PEndpoint) NetworkEventP2PEndpoint { + if proto == nil { + return NetworkEventP2PEndpoint{} + } + return NetworkEventP2PEndpoint{ + Hash: proto.Hash, + Port: int(proto.Port), + Fields: ipFieldsFromProto(proto.Fields), + } +} + +type DERPMapHomeParams struct { + RegionScore map[int64]float64 `json:"region_score"` +} + +func derpMapHomeParamsFromProto(proto *tailnetproto.DERPMap_HomeParams) DERPMapHomeParams { + if proto == nil { + return DERPMapHomeParams{} + } + out := DERPMapHomeParams{ + RegionScore: make(map[int64]float64, len(proto.RegionScore)), + } + for k, v := range proto.RegionScore { + out.RegionScore[k] = v + } + return out +} + +type DERPRegion struct { + RegionID int64 `json:"region_id"` + EmbeddedRelay bool `json:"embedded_relay"` + RegionCode string + RegionName string + Avoid bool + Nodes []DERPNode `json:"nodes"` +} + +func derpRegionFromProto(proto *tailnetproto.DERPMap_Region) DERPRegion { + if proto == nil { + return DERPRegion{} + } + nodes := make([]DERPNode, 0, len(proto.Nodes)) + for _, node := range proto.Nodes { + nodes = append(nodes, derpNodeFromProto(node)) + } + return DERPRegion{ + RegionID: proto.RegionId, + EmbeddedRelay: proto.EmbeddedRelay, + RegionCode: proto.RegionCode, + RegionName: proto.RegionName, + Avoid: proto.Avoid, + Nodes: nodes, + } +} + +type DERPNode struct { + Name string `json:"name"` + RegionID int64 `json:"region_id"` + HostName string `json:"host_name"` + CertName string `json:"cert_name"` + IPv4 string `json:"ipv4"` + IPv6 string `json:"ipv6"` + STUNPort int32 `json:"stun_port"` + STUNOnly bool `json:"stun_only"` + DERPPort int32 `json:"derp_port"` + InsecureForTests bool `json:"insecure_for_tests"` + ForceHTTP bool `json:"force_http"` + STUNTestIP string `json:"stun_test_ip"` + CanPort80 bool `json:"can_port_80"` +} + +func derpNodeFromProto(proto *tailnetproto.DERPMap_Region_Node) DERPNode { + if proto == nil { + return DERPNode{} + } + return DERPNode{ + Name: proto.Name, + RegionID: proto.RegionId, + HostName: proto.HostName, + CertName: proto.CertName, + IPv4: proto.Ipv4, + IPv6: proto.Ipv6, + STUNPort: proto.StunPort, + STUNOnly: proto.StunOnly, + DERPPort: proto.DerpPort, + InsecureForTests: proto.InsecureForTests, + ForceHTTP: proto.ForceHttp, + STUNTestIP: proto.StunTestIp, + CanPort80: proto.CanPort_80, + } +} + +type DERPMap struct { + HomeParams DERPMapHomeParams `json:"home_params"` + Regions map[int64]DERPRegion +} + +func derpMapFromProto(proto *tailnetproto.DERPMap) DERPMap { + if proto == nil { + return DERPMap{} + } + regionMap := make(map[int64]DERPRegion, len(proto.Regions)) + for k, v := range proto.Regions { + regionMap[k] = derpRegionFromProto(v) + } + return DERPMap{ + HomeParams: derpMapHomeParamsFromProto(proto.HomeParams), + Regions: regionMap, + } +} + +type NetcheckIP struct { + Hash string `json:"hash"` + Fields NetworkEventIPFields `json:"fields"` +} + +func netcheckIPFromProto(proto *tailnetproto.Netcheck_NetcheckIP) NetcheckIP { + if proto == nil { + return NetcheckIP{} + } + return NetcheckIP{ + Hash: proto.Hash, + Fields: ipFieldsFromProto(proto.Fields), + } +} + +type Netcheck struct { + UDP bool `json:"udp"` + IPv6 bool `json:"ipv6"` + IPv4 bool `json:"ipv4"` + IPv6CanSend bool `json:"ipv6_can_send"` + IPv4CanSend bool `json:"ipv4_can_send"` + OSHasIPv6 bool `json:"os_has_ipv6"` + ICMPv4 bool `json:"icmpv4"` + + MappingVariesByDestIP *bool `json:"mapping_varies_by_dest_ip"` + HairPinning *bool `json:"hair_pinning"` + UPnP *bool `json:"upnp"` + PMP *bool `json:"pmp"` + PCP *bool `json:"pcp"` + + PreferredDERP int64 `json:"preferred_derp"` + + RegionLatency map[int64]time.Duration `json:"region_latency"` + RegionV4Latency map[int64]time.Duration `json:"region_v4_latency"` + RegionV6Latency map[int64]time.Duration `json:"region_v6_latency"` + + GlobalV4 NetcheckIP `json:"global_v4"` + GlobalV6 NetcheckIP `json:"global_v6"` + + CaptivePortal *bool `json:"captive_portal"` +} + +func protoBool(b *wrapperspb.BoolValue) *bool { + if b == nil { + return nil + } + return &b.Value +} + +func netcheckFromProto(proto *tailnetproto.Netcheck) Netcheck { + if proto == nil { + return Netcheck{} + } + + durationMapFromProto := func(m map[int64]*durationpb.Duration) map[int64]time.Duration { + out := make(map[int64]time.Duration, len(m)) + for k, v := range m { + out[k] = v.AsDuration() + } + return out + } + + return Netcheck{ + UDP: proto.UDP, + IPv6: proto.IPv6, + IPv4: proto.IPv4, + IPv6CanSend: proto.IPv6CanSend, + IPv4CanSend: proto.IPv4CanSend, + OSHasIPv6: proto.OSHasIPv6, + ICMPv4: proto.ICMPv4, + + MappingVariesByDestIP: protoBool(proto.MappingVariesByDestIP), + HairPinning: protoBool(proto.HairPinning), + UPnP: protoBool(proto.UPnP), + PMP: protoBool(proto.PMP), + PCP: protoBool(proto.PCP), + + PreferredDERP: proto.PreferredDERP, + + RegionLatency: durationMapFromProto(proto.RegionLatency), + RegionV4Latency: durationMapFromProto(proto.RegionV4Latency), + RegionV6Latency: durationMapFromProto(proto.RegionV6Latency), + + GlobalV4: netcheckIPFromProto(proto.GlobalV4), + GlobalV6: netcheckIPFromProto(proto.GlobalV6), + + CaptivePortal: protoBool(proto.CaptivePortal), + } +} + +// NetworkEvent and all related structs come from tailnet.proto. +type NetworkEvent struct { + ID uuid.UUID `json:"id"` + Time time.Time `json:"time"` + Application string `json:"application"` + Status string `json:"status"` // connected, disconnected + DisconnectionReason string `json:"disconnection_reason"` + ClientType string `json:"client_type"` // cli, agent, coderd, wsproxy + NodeIDSelf uuid.UUID `json:"node_id_self"` + NodeIDRemote uuid.UUID `json:"node_id_remote"` + P2PEndpoint NetworkEventP2PEndpoint `json:"p2p_endpoint"` + LogIPHashes map[string]NetworkEventIPFields `json:"log_ip_hashes"` + HomeDERP string `json:"home_derp"` + Logs []string `json:"logs"` + DERPMap DERPMap `json:"derp_map"` + LatestNetcheck Netcheck `json:"latest_netcheck"` + + ConnectionAge time.Duration `json:"connection_age"` + ConnectionSetup time.Duration `json:"connection_setup"` + P2PSetup time.Duration `json:"p2p_setup"` + DERPLatency time.Duration `json:"derp_latency"` + P2PLatency time.Duration `json:"p2p_latency"` + ThroughputMbits *float32 `json:"throughput_mbits"` +} + +func protoFloat(f *wrapperspb.FloatValue) *float32 { + if f == nil { + return nil + } + return &f.Value +} + +func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, error) { + if proto == nil { + return NetworkEvent{}, xerrors.New("nil event") + } + id, err := uuid.ParseBytes(proto.Id) + if err != nil { + return NetworkEvent{}, xerrors.Errorf("parse id %q: %w", proto.Id, err) + } + nodeIDSelf, err := uuid.ParseBytes(proto.NodeIdSelf) + if err != nil { + return NetworkEvent{}, xerrors.Errorf("parse node_id_self %q: %w", proto.NodeIdSelf, err) + } + nodeIDRemote, err := uuid.ParseBytes(proto.NodeIdRemote) + if err != nil { + return NetworkEvent{}, xerrors.Errorf("parse node_id_remote %q: %w", proto.NodeIdRemote, err) + } + + logIPHashes := make(map[string]NetworkEventIPFields, len(proto.LogIpHashes)) + for k, v := range proto.LogIpHashes { + logIPHashes[k] = ipFieldsFromProto(v) + } + + return NetworkEvent{ + ID: id, + Time: proto.Time.AsTime(), + Application: proto.Application, + Status: strings.ToLower(proto.Status.String()), + DisconnectionReason: proto.DisconnectionReason, + ClientType: strings.ToLower(proto.ClientType.String()), + NodeIDSelf: nodeIDSelf, + NodeIDRemote: nodeIDRemote, + P2PEndpoint: p2pEndpointFromProto(proto.P2PEndpoint), + LogIPHashes: logIPHashes, + HomeDERP: proto.HomeDerp, + Logs: proto.Logs, + DERPMap: derpMapFromProto(proto.DerpMap), + LatestNetcheck: netcheckFromProto(proto.LatestNetcheck), + + ConnectionAge: proto.ConnectionAge.AsDuration(), + ConnectionSetup: proto.ConnectionSetup.AsDuration(), + P2PSetup: proto.P2PSetup.AsDuration(), + DERPLatency: proto.DerpLatency.AsDuration(), + P2PLatency: proto.P2PLatency.AsDuration(), + ThroughputMbits: protoFloat(proto.ThroughputMbits), + }, nil +} + type noopReporter struct{} func (*noopReporter) Report(_ *Snapshot) {} diff --git a/coderd/workspaceagentsrpc.go b/coderd/workspaceagentsrpc.go index b413db264feac..9853d170eff7c 100644 --- a/coderd/workspaceagentsrpc.go +++ b/coderd/workspaceagentsrpc.go @@ -24,6 +24,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtime" "github.com/coder/coder/v2/coderd/httpapi" "github.com/coder/coder/v2/coderd/httpmw" + "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/tailnet" @@ -135,15 +136,22 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { StatsReporter: api.statsReporter, PublishWorkspaceUpdateFn: api.publishWorkspaceUpdate, PublishWorkspaceAgentLogsUpdateFn: api.publishWorkspaceAgentLogsUpdate, + NetworkTelemetryBatchFn: func(batch []telemetry.NetworkEvent) { + api.Telemetry.Report(&telemetry.Snapshot{ + NetworkEvents: batch, + }) + }, - AccessURL: api.AccessURL, - AppHostname: api.AppHostname, - AgentStatsRefreshInterval: api.AgentStatsRefreshInterval, - DisableDirectConnections: api.DeploymentValues.DERP.Config.BlockDirect.Value(), - DerpForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(), - DerpMapUpdateFrequency: api.Options.DERPMapUpdateFrequency, - ExternalAuthConfigs: api.ExternalAuthConfigs, - Experiments: api.Experiments, + AccessURL: api.AccessURL, + AppHostname: api.AppHostname, + AgentStatsRefreshInterval: api.AgentStatsRefreshInterval, + DisableDirectConnections: api.DeploymentValues.DERP.Config.BlockDirect.Value(), + DerpForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(), + DerpMapUpdateFrequency: api.Options.DERPMapUpdateFrequency, + NetworkTelemetryBatchFrequency: api.Options.NetworkTelemetryBatchFrequency, + NetworkTelemetryBatchMaxSize: api.Options.NetworkTelemetryBatchMaxSize, + ExternalAuthConfigs: api.ExternalAuthConfigs, + Experiments: api.Experiments, // Optional: WorkspaceID: build.WorkspaceID, // saves the extra lookup later diff --git a/tailnet/proto/tailnet.pb.go b/tailnet/proto/tailnet.pb.go index c2dfd88a483ba..f293b7ba69f55 100644 --- a/tailnet/proto/tailnet.pb.go +++ b/tailnet/proto/tailnet.pb.go @@ -78,6 +78,61 @@ func (CoordinateResponse_PeerUpdate_Kind) EnumDescriptor() ([]byte, []int) { return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{4, 0, 0} } +type IPFields_IPClass int32 + +const ( + IPFields_PUBLIC IPFields_IPClass = 0 + IPFields_PRIVATE IPFields_IPClass = 1 + IPFields_LINK_LOCAL IPFields_IPClass = 2 + IPFields_UNIQUE_LOCAL IPFields_IPClass = 3 + IPFields_LOOPBACK IPFields_IPClass = 4 +) + +// Enum value maps for IPFields_IPClass. +var ( + IPFields_IPClass_name = map[int32]string{ + 0: "PUBLIC", + 1: "PRIVATE", + 2: "LINK_LOCAL", + 3: "UNIQUE_LOCAL", + 4: "LOOPBACK", + } + IPFields_IPClass_value = map[string]int32{ + "PUBLIC": 0, + "PRIVATE": 1, + "LINK_LOCAL": 2, + "UNIQUE_LOCAL": 3, + "LOOPBACK": 4, + } +) + +func (x IPFields_IPClass) Enum() *IPFields_IPClass { + p := new(IPFields_IPClass) + *p = x + return p +} + +func (x IPFields_IPClass) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (IPFields_IPClass) Descriptor() protoreflect.EnumDescriptor { + return file_tailnet_proto_tailnet_proto_enumTypes[1].Descriptor() +} + +func (IPFields_IPClass) Type() protoreflect.EnumType { + return &file_tailnet_proto_tailnet_proto_enumTypes[1] +} + +func (x IPFields_IPClass) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use IPFields_IPClass.Descriptor instead. +func (IPFields_IPClass) EnumDescriptor() ([]byte, []int) { + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{5, 0} +} + type TelemetryEvent_Status int32 const ( @@ -108,11 +163,11 @@ func (x TelemetryEvent_Status) String() string { } func (TelemetryEvent_Status) Descriptor() protoreflect.EnumDescriptor { - return file_tailnet_proto_tailnet_proto_enumTypes[1].Descriptor() + return file_tailnet_proto_tailnet_proto_enumTypes[2].Descriptor() } func (TelemetryEvent_Status) Type() protoreflect.EnumType { - return &file_tailnet_proto_tailnet_proto_enumTypes[1] + return &file_tailnet_proto_tailnet_proto_enumTypes[2] } func (x TelemetryEvent_Status) Number() protoreflect.EnumNumber { @@ -121,7 +176,7 @@ func (x TelemetryEvent_Status) Number() protoreflect.EnumNumber { // Deprecated: Use TelemetryEvent_Status.Descriptor instead. func (TelemetryEvent_Status) EnumDescriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6, 0} + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{7, 0} } type TelemetryEvent_ClientType int32 @@ -160,11 +215,11 @@ func (x TelemetryEvent_ClientType) String() string { } func (TelemetryEvent_ClientType) Descriptor() protoreflect.EnumDescriptor { - return file_tailnet_proto_tailnet_proto_enumTypes[2].Descriptor() + return file_tailnet_proto_tailnet_proto_enumTypes[3].Descriptor() } func (TelemetryEvent_ClientType) Type() protoreflect.EnumType { - return &file_tailnet_proto_tailnet_proto_enumTypes[2] + return &file_tailnet_proto_tailnet_proto_enumTypes[3] } func (x TelemetryEvent_ClientType) Number() protoreflect.EnumNumber { @@ -173,62 +228,7 @@ func (x TelemetryEvent_ClientType) Number() protoreflect.EnumNumber { // Deprecated: Use TelemetryEvent_ClientType.Descriptor instead. func (TelemetryEvent_ClientType) EnumDescriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6, 1} -} - -type TelemetryEvent_IPClass int32 - -const ( - TelemetryEvent_PUBLIC TelemetryEvent_IPClass = 0 - TelemetryEvent_PRIVATE TelemetryEvent_IPClass = 1 - TelemetryEvent_LINK_LOCAL TelemetryEvent_IPClass = 2 - TelemetryEvent_UNIQUE_LOCAL TelemetryEvent_IPClass = 3 - TelemetryEvent_LOOPBACK TelemetryEvent_IPClass = 4 -) - -// Enum value maps for TelemetryEvent_IPClass. -var ( - TelemetryEvent_IPClass_name = map[int32]string{ - 0: "PUBLIC", - 1: "PRIVATE", - 2: "LINK_LOCAL", - 3: "UNIQUE_LOCAL", - 4: "LOOPBACK", - } - TelemetryEvent_IPClass_value = map[string]int32{ - "PUBLIC": 0, - "PRIVATE": 1, - "LINK_LOCAL": 2, - "UNIQUE_LOCAL": 3, - "LOOPBACK": 4, - } -) - -func (x TelemetryEvent_IPClass) Enum() *TelemetryEvent_IPClass { - p := new(TelemetryEvent_IPClass) - *p = x - return p -} - -func (x TelemetryEvent_IPClass) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (TelemetryEvent_IPClass) Descriptor() protoreflect.EnumDescriptor { - return file_tailnet_proto_tailnet_proto_enumTypes[3].Descriptor() -} - -func (TelemetryEvent_IPClass) Type() protoreflect.EnumType { - return &file_tailnet_proto_tailnet_proto_enumTypes[3] -} - -func (x TelemetryEvent_IPClass) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use TelemetryEvent_IPClass.Descriptor instead. -func (TelemetryEvent_IPClass) EnumDescriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6, 2} + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{7, 1} } type DERPMap struct { @@ -578,6 +578,61 @@ func (x *CoordinateResponse) GetError() string { return "" } +type IPFields struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + Class IPFields_IPClass `protobuf:"varint,2,opt,name=class,proto3,enum=coder.tailnet.v2.IPFields_IPClass" json:"class,omitempty"` +} + +func (x *IPFields) Reset() { + *x = IPFields{} + if protoimpl.UnsafeEnabled { + mi := &file_tailnet_proto_tailnet_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *IPFields) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*IPFields) ProtoMessage() {} + +func (x *IPFields) ProtoReflect() protoreflect.Message { + mi := &file_tailnet_proto_tailnet_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use IPFields.ProtoReflect.Descriptor instead. +func (*IPFields) Descriptor() ([]byte, []int) { + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{5} +} + +func (x *IPFields) GetVersion() int32 { + if x != nil { + return x.Version + } + return 0 +} + +func (x *IPFields) GetClass() IPFields_IPClass { + if x != nil { + return x.Class + } + return IPFields_PUBLIC +} + type Netcheck struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -599,15 +654,15 @@ type Netcheck struct { RegionLatency map[int64]*durationpb.Duration `protobuf:"bytes,14,rep,name=RegionLatency,proto3" json:"RegionLatency,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` RegionV4Latency map[int64]*durationpb.Duration `protobuf:"bytes,15,rep,name=RegionV4Latency,proto3" json:"RegionV4Latency,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` RegionV6Latency map[int64]*durationpb.Duration `protobuf:"bytes,16,rep,name=RegionV6Latency,proto3" json:"RegionV6Latency,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - GlobalV4 string `protobuf:"bytes,17,opt,name=GlobalV4,proto3" json:"GlobalV4,omitempty"` - GlobalV6 string `protobuf:"bytes,18,opt,name=GlobalV6,proto3" json:"GlobalV6,omitempty"` + GlobalV4 *Netcheck_NetcheckIP `protobuf:"bytes,17,opt,name=GlobalV4,proto3" json:"GlobalV4,omitempty"` + GlobalV6 *Netcheck_NetcheckIP `protobuf:"bytes,18,opt,name=GlobalV6,proto3" json:"GlobalV6,omitempty"` CaptivePortal *wrapperspb.BoolValue `protobuf:"bytes,19,opt,name=CaptivePortal,proto3" json:"CaptivePortal,omitempty"` } func (x *Netcheck) Reset() { *x = Netcheck{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[5] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -620,7 +675,7 @@ func (x *Netcheck) String() string { func (*Netcheck) ProtoMessage() {} func (x *Netcheck) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[5] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -633,7 +688,7 @@ func (x *Netcheck) ProtoReflect() protoreflect.Message { // Deprecated: Use Netcheck.ProtoReflect.Descriptor instead. func (*Netcheck) Descriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{5} + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6} } func (x *Netcheck) GetUDP() bool { @@ -748,18 +803,18 @@ func (x *Netcheck) GetRegionV6Latency() map[int64]*durationpb.Duration { return nil } -func (x *Netcheck) GetGlobalV4() string { +func (x *Netcheck) GetGlobalV4() *Netcheck_NetcheckIP { if x != nil { return x.GlobalV4 } - return "" + return nil } -func (x *Netcheck) GetGlobalV6() string { +func (x *Netcheck) GetGlobalV6() *Netcheck_NetcheckIP { if x != nil { return x.GlobalV6 } - return "" + return nil } func (x *Netcheck) GetCaptivePortal() *wrapperspb.BoolValue { @@ -774,32 +829,32 @@ type TelemetryEvent struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Time *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=time,proto3" json:"time,omitempty"` - Application string `protobuf:"bytes,3,opt,name=application,proto3" json:"application,omitempty"` - Status TelemetryEvent_Status `protobuf:"varint,4,opt,name=status,proto3,enum=coder.tailnet.v2.TelemetryEvent_Status" json:"status,omitempty"` - DisconnectionReason string `protobuf:"bytes,5,opt,name=disconnection_reason,json=disconnectionReason,proto3" json:"disconnection_reason,omitempty"` - ClientType TelemetryEvent_ClientType `protobuf:"varint,6,opt,name=client_type,json=clientType,proto3,enum=coder.tailnet.v2.TelemetryEvent_ClientType" json:"client_type,omitempty"` - NodeIdSelf string `protobuf:"bytes,7,opt,name=node_id_self,json=nodeIdSelf,proto3" json:"node_id_self,omitempty"` - NodeIdRemote string `protobuf:"bytes,8,opt,name=node_id_remote,json=nodeIdRemote,proto3" json:"node_id_remote,omitempty"` - P2PEndpoint *TelemetryEvent_P2PEndpoint `protobuf:"bytes,9,opt,name=p2p_endpoint,json=p2pEndpoint,proto3" json:"p2p_endpoint,omitempty"` - LogIpHashes map[string]*TelemetryEvent_IPFields `protobuf:"bytes,10,rep,name=log_ip_hashes,json=logIpHashes,proto3" json:"log_ip_hashes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - HomeDerp string `protobuf:"bytes,11,opt,name=home_derp,json=homeDerp,proto3" json:"home_derp,omitempty"` - Logs []string `protobuf:"bytes,12,rep,name=logs,proto3" json:"logs,omitempty"` - DerpMap *DERPMap `protobuf:"bytes,13,opt,name=derp_map,json=derpMap,proto3" json:"derp_map,omitempty"` - LatestNetcheck *Netcheck `protobuf:"bytes,14,opt,name=latest_netcheck,json=latestNetcheck,proto3" json:"latest_netcheck,omitempty"` - ConnectionAge *durationpb.Duration `protobuf:"bytes,15,opt,name=connection_age,json=connectionAge,proto3" json:"connection_age,omitempty"` - ConnectionSetup *durationpb.Duration `protobuf:"bytes,16,opt,name=connection_setup,json=connectionSetup,proto3" json:"connection_setup,omitempty"` - P2PSetup *durationpb.Duration `protobuf:"bytes,17,opt,name=p2p_setup,json=p2pSetup,proto3" json:"p2p_setup,omitempty"` - DerpLatency *durationpb.Duration `protobuf:"bytes,18,opt,name=derp_latency,json=derpLatency,proto3" json:"derp_latency,omitempty"` - P2PLatency *durationpb.Duration `protobuf:"bytes,19,opt,name=p2p_latency,json=p2pLatency,proto3" json:"p2p_latency,omitempty"` - ThroughputMbits *wrapperspb.FloatValue `protobuf:"bytes,20,opt,name=throughput_mbits,json=throughputMbits,proto3" json:"throughput_mbits,omitempty"` + Id []byte `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Time *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=time,proto3" json:"time,omitempty"` + Application string `protobuf:"bytes,3,opt,name=application,proto3" json:"application,omitempty"` + Status TelemetryEvent_Status `protobuf:"varint,4,opt,name=status,proto3,enum=coder.tailnet.v2.TelemetryEvent_Status" json:"status,omitempty"` + DisconnectionReason string `protobuf:"bytes,5,opt,name=disconnection_reason,json=disconnectionReason,proto3" json:"disconnection_reason,omitempty"` + ClientType TelemetryEvent_ClientType `protobuf:"varint,6,opt,name=client_type,json=clientType,proto3,enum=coder.tailnet.v2.TelemetryEvent_ClientType" json:"client_type,omitempty"` + NodeIdSelf []byte `protobuf:"bytes,7,opt,name=node_id_self,json=nodeIdSelf,proto3" json:"node_id_self,omitempty"` + NodeIdRemote []byte `protobuf:"bytes,8,opt,name=node_id_remote,json=nodeIdRemote,proto3" json:"node_id_remote,omitempty"` + P2PEndpoint *TelemetryEvent_P2PEndpoint `protobuf:"bytes,9,opt,name=p2p_endpoint,json=p2pEndpoint,proto3" json:"p2p_endpoint,omitempty"` + LogIpHashes map[string]*IPFields `protobuf:"bytes,10,rep,name=log_ip_hashes,json=logIpHashes,proto3" json:"log_ip_hashes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + HomeDerp string `protobuf:"bytes,11,opt,name=home_derp,json=homeDerp,proto3" json:"home_derp,omitempty"` + Logs []string `protobuf:"bytes,12,rep,name=logs,proto3" json:"logs,omitempty"` + DerpMap *DERPMap `protobuf:"bytes,13,opt,name=derp_map,json=derpMap,proto3" json:"derp_map,omitempty"` + LatestNetcheck *Netcheck `protobuf:"bytes,14,opt,name=latest_netcheck,json=latestNetcheck,proto3" json:"latest_netcheck,omitempty"` + ConnectionAge *durationpb.Duration `protobuf:"bytes,15,opt,name=connection_age,json=connectionAge,proto3" json:"connection_age,omitempty"` + ConnectionSetup *durationpb.Duration `protobuf:"bytes,16,opt,name=connection_setup,json=connectionSetup,proto3" json:"connection_setup,omitempty"` + P2PSetup *durationpb.Duration `protobuf:"bytes,17,opt,name=p2p_setup,json=p2pSetup,proto3" json:"p2p_setup,omitempty"` + DerpLatency *durationpb.Duration `protobuf:"bytes,18,opt,name=derp_latency,json=derpLatency,proto3" json:"derp_latency,omitempty"` + P2PLatency *durationpb.Duration `protobuf:"bytes,19,opt,name=p2p_latency,json=p2pLatency,proto3" json:"p2p_latency,omitempty"` + ThroughputMbits *wrapperspb.FloatValue `protobuf:"bytes,20,opt,name=throughput_mbits,json=throughputMbits,proto3" json:"throughput_mbits,omitempty"` } func (x *TelemetryEvent) Reset() { *x = TelemetryEvent{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[6] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -812,7 +867,7 @@ func (x *TelemetryEvent) String() string { func (*TelemetryEvent) ProtoMessage() {} func (x *TelemetryEvent) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[6] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -825,7 +880,7 @@ func (x *TelemetryEvent) ProtoReflect() protoreflect.Message { // Deprecated: Use TelemetryEvent.ProtoReflect.Descriptor instead. func (*TelemetryEvent) Descriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6} + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{7} } func (x *TelemetryEvent) GetId() []byte { @@ -870,18 +925,18 @@ func (x *TelemetryEvent) GetClientType() TelemetryEvent_ClientType { return TelemetryEvent_CLI } -func (x *TelemetryEvent) GetNodeIdSelf() string { +func (x *TelemetryEvent) GetNodeIdSelf() []byte { if x != nil { return x.NodeIdSelf } - return "" + return nil } -func (x *TelemetryEvent) GetNodeIdRemote() string { +func (x *TelemetryEvent) GetNodeIdRemote() []byte { if x != nil { return x.NodeIdRemote } - return "" + return nil } func (x *TelemetryEvent) GetP2PEndpoint() *TelemetryEvent_P2PEndpoint { @@ -891,7 +946,7 @@ func (x *TelemetryEvent) GetP2PEndpoint() *TelemetryEvent_P2PEndpoint { return nil } -func (x *TelemetryEvent) GetLogIpHashes() map[string]*TelemetryEvent_IPFields { +func (x *TelemetryEvent) GetLogIpHashes() map[string]*IPFields { if x != nil { return x.LogIpHashes } @@ -979,7 +1034,7 @@ type TelemetryRequest struct { func (x *TelemetryRequest) Reset() { *x = TelemetryRequest{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[7] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -992,7 +1047,7 @@ func (x *TelemetryRequest) String() string { func (*TelemetryRequest) ProtoMessage() {} func (x *TelemetryRequest) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[7] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1005,7 +1060,7 @@ func (x *TelemetryRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use TelemetryRequest.ProtoReflect.Descriptor instead. func (*TelemetryRequest) Descriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{7} + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{8} } func (x *TelemetryRequest) GetEvents() []*TelemetryEvent { @@ -1024,7 +1079,7 @@ type TelemetryResponse struct { func (x *TelemetryResponse) Reset() { *x = TelemetryResponse{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[8] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1037,7 +1092,7 @@ func (x *TelemetryResponse) String() string { func (*TelemetryResponse) ProtoMessage() {} func (x *TelemetryResponse) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[8] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[9] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1050,7 +1105,7 @@ func (x *TelemetryResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TelemetryResponse.ProtoReflect.Descriptor instead. func (*TelemetryResponse) Descriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{8} + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{9} } type DERPMap_HomeParams struct { @@ -1064,7 +1119,7 @@ type DERPMap_HomeParams struct { func (x *DERPMap_HomeParams) Reset() { *x = DERPMap_HomeParams{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[9] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1077,7 +1132,7 @@ func (x *DERPMap_HomeParams) String() string { func (*DERPMap_HomeParams) ProtoMessage() {} func (x *DERPMap_HomeParams) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[9] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1116,7 +1171,7 @@ type DERPMap_Region struct { func (x *DERPMap_Region) Reset() { *x = DERPMap_Region{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[10] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1129,7 +1184,7 @@ func (x *DERPMap_Region) String() string { func (*DERPMap_Region) ProtoMessage() {} func (x *DERPMap_Region) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[10] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1210,7 +1265,7 @@ type DERPMap_Region_Node struct { func (x *DERPMap_Region_Node) Reset() { *x = DERPMap_Region_Node{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[13] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1223,7 +1278,7 @@ func (x *DERPMap_Region_Node) String() string { func (*DERPMap_Region_Node) ProtoMessage() {} func (x *DERPMap_Region_Node) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[13] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1341,7 +1396,7 @@ type CoordinateRequest_UpdateSelf struct { func (x *CoordinateRequest_UpdateSelf) Reset() { *x = CoordinateRequest_UpdateSelf{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[16] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1354,7 +1409,7 @@ func (x *CoordinateRequest_UpdateSelf) String() string { func (*CoordinateRequest_UpdateSelf) ProtoMessage() {} func (x *CoordinateRequest_UpdateSelf) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[16] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1386,7 +1441,7 @@ type CoordinateRequest_Disconnect struct { func (x *CoordinateRequest_Disconnect) Reset() { *x = CoordinateRequest_Disconnect{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[17] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1399,7 +1454,7 @@ func (x *CoordinateRequest_Disconnect) String() string { func (*CoordinateRequest_Disconnect) ProtoMessage() {} func (x *CoordinateRequest_Disconnect) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[17] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1426,7 +1481,7 @@ type CoordinateRequest_Tunnel struct { func (x *CoordinateRequest_Tunnel) Reset() { *x = CoordinateRequest_Tunnel{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[18] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1439,7 +1494,7 @@ func (x *CoordinateRequest_Tunnel) String() string { func (*CoordinateRequest_Tunnel) ProtoMessage() {} func (x *CoordinateRequest_Tunnel) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[18] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1477,7 +1532,7 @@ type CoordinateRequest_ReadyForHandshake struct { func (x *CoordinateRequest_ReadyForHandshake) Reset() { *x = CoordinateRequest_ReadyForHandshake{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[19] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1490,7 +1545,7 @@ func (x *CoordinateRequest_ReadyForHandshake) String() string { func (*CoordinateRequest_ReadyForHandshake) ProtoMessage() {} func (x *CoordinateRequest_ReadyForHandshake) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[19] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1527,7 +1582,7 @@ type CoordinateResponse_PeerUpdate struct { func (x *CoordinateResponse_PeerUpdate) Reset() { *x = CoordinateResponse_PeerUpdate{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[20] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1540,7 +1595,7 @@ func (x *CoordinateResponse_PeerUpdate) String() string { func (*CoordinateResponse_PeerUpdate) ProtoMessage() {} func (x *CoordinateResponse_PeerUpdate) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[20] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1584,32 +1639,32 @@ func (x *CoordinateResponse_PeerUpdate) GetReason() string { return "" } -type TelemetryEvent_IPFields struct { +type Netcheck_NetcheckIP struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Version int32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` - Class TelemetryEvent_IPClass `protobuf:"varint,2,opt,name=class,proto3,enum=coder.tailnet.v2.TelemetryEvent_IPClass" json:"class,omitempty"` + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Fields *IPFields `protobuf:"bytes,2,opt,name=fields,proto3" json:"fields,omitempty"` } -func (x *TelemetryEvent_IPFields) Reset() { - *x = TelemetryEvent_IPFields{} +func (x *Netcheck_NetcheckIP) Reset() { + *x = Netcheck_NetcheckIP{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[24] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *TelemetryEvent_IPFields) String() string { +func (x *Netcheck_NetcheckIP) String() string { return protoimpl.X.MessageStringOf(x) } -func (*TelemetryEvent_IPFields) ProtoMessage() {} +func (*Netcheck_NetcheckIP) ProtoMessage() {} -func (x *TelemetryEvent_IPFields) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[24] +func (x *Netcheck_NetcheckIP) ProtoReflect() protoreflect.Message { + mi := &file_tailnet_proto_tailnet_proto_msgTypes[25] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1620,23 +1675,23 @@ func (x *TelemetryEvent_IPFields) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use TelemetryEvent_IPFields.ProtoReflect.Descriptor instead. -func (*TelemetryEvent_IPFields) Descriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6, 0} +// Deprecated: Use Netcheck_NetcheckIP.ProtoReflect.Descriptor instead. +func (*Netcheck_NetcheckIP) Descriptor() ([]byte, []int) { + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6, 3} } -func (x *TelemetryEvent_IPFields) GetVersion() int32 { +func (x *Netcheck_NetcheckIP) GetHash() string { if x != nil { - return x.Version + return x.Hash } - return 0 + return "" } -func (x *TelemetryEvent_IPFields) GetClass() TelemetryEvent_IPClass { +func (x *Netcheck_NetcheckIP) GetFields() *IPFields { if x != nil { - return x.Class + return x.Fields } - return TelemetryEvent_PUBLIC + return nil } type TelemetryEvent_P2PEndpoint struct { @@ -1644,15 +1699,15 @@ type TelemetryEvent_P2PEndpoint struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` - Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` - Fields *TelemetryEvent_IPFields `protobuf:"bytes,3,opt,name=fields,proto3" json:"fields,omitempty"` + Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` + Port int32 `protobuf:"varint,2,opt,name=port,proto3" json:"port,omitempty"` + Fields *IPFields `protobuf:"bytes,3,opt,name=fields,proto3" json:"fields,omitempty"` } func (x *TelemetryEvent_P2PEndpoint) Reset() { *x = TelemetryEvent_P2PEndpoint{} if protoimpl.UnsafeEnabled { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[25] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1665,7 +1720,7 @@ func (x *TelemetryEvent_P2PEndpoint) String() string { func (*TelemetryEvent_P2PEndpoint) ProtoMessage() {} func (x *TelemetryEvent_P2PEndpoint) ProtoReflect() protoreflect.Message { - mi := &file_tailnet_proto_tailnet_proto_msgTypes[25] + mi := &file_tailnet_proto_tailnet_proto_msgTypes[26] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1678,7 +1733,7 @@ func (x *TelemetryEvent_P2PEndpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use TelemetryEvent_P2PEndpoint.ProtoReflect.Descriptor instead. func (*TelemetryEvent_P2PEndpoint) Descriptor() ([]byte, []int) { - return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{6, 1} + return file_tailnet_proto_tailnet_proto_rawDescGZIP(), []int{7, 0} } func (x *TelemetryEvent_P2PEndpoint) GetHash() string { @@ -1695,7 +1750,7 @@ func (x *TelemetryEvent_P2PEndpoint) GetPort() int32 { return 0 } -func (x *TelemetryEvent_P2PEndpoint) GetFields() *TelemetryEvent_IPFields { +func (x *TelemetryEvent_P2PEndpoint) GetFields() *IPFields { if x != nil { return x.Fields } @@ -1875,210 +1930,218 @@ var file_tailnet_proto_tailnet_proto_rawDesc = []byte{ 0x01, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4c, 0x4f, 0x53, 0x54, 0x10, 0x03, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x41, 0x44, 0x59, 0x5f, 0x46, 0x4f, 0x52, 0x5f, 0x48, 0x41, 0x4e, 0x44, 0x53, - 0x48, 0x41, 0x4b, 0x45, 0x10, 0x04, 0x22, 0xa0, 0x09, 0x0a, 0x08, 0x4e, 0x65, 0x74, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x03, 0x55, 0x44, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x49, 0x50, 0x76, 0x36, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x04, 0x49, 0x50, 0x76, 0x36, 0x12, 0x12, 0x0a, 0x04, 0x49, 0x50, 0x76, - 0x34, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x49, 0x50, 0x76, 0x34, 0x12, 0x20, 0x0a, - 0x0b, 0x49, 0x50, 0x76, 0x36, 0x43, 0x61, 0x6e, 0x53, 0x65, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0b, 0x49, 0x50, 0x76, 0x36, 0x43, 0x61, 0x6e, 0x53, 0x65, 0x6e, 0x64, 0x12, - 0x20, 0x0a, 0x0b, 0x49, 0x50, 0x76, 0x34, 0x43, 0x61, 0x6e, 0x53, 0x65, 0x6e, 0x64, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x50, 0x76, 0x34, 0x43, 0x61, 0x6e, 0x53, 0x65, 0x6e, - 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x4f, 0x53, 0x48, 0x61, 0x73, 0x49, 0x50, 0x76, 0x36, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x4f, 0x53, 0x48, 0x61, 0x73, 0x49, 0x50, 0x76, 0x36, 0x12, - 0x16, 0x0a, 0x06, 0x49, 0x43, 0x4d, 0x50, 0x76, 0x34, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x06, 0x49, 0x43, 0x4d, 0x50, 0x76, 0x34, 0x12, 0x50, 0x0a, 0x15, 0x4d, 0x61, 0x70, 0x70, 0x69, - 0x6e, 0x67, 0x56, 0x61, 0x72, 0x69, 0x65, 0x73, 0x42, 0x79, 0x44, 0x65, 0x73, 0x74, 0x49, 0x50, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, - 0x75, 0x65, 0x52, 0x15, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x72, 0x69, 0x65, - 0x73, 0x42, 0x79, 0x44, 0x65, 0x73, 0x74, 0x49, 0x50, 0x12, 0x3c, 0x0a, 0x0b, 0x48, 0x61, 0x69, - 0x72, 0x50, 0x69, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0b, 0x48, 0x61, 0x69, 0x72, - 0x50, 0x69, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x2e, 0x0a, 0x04, 0x55, 0x50, 0x6e, 0x50, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x52, 0x04, 0x55, 0x50, 0x6e, 0x50, 0x12, 0x2c, 0x0a, 0x03, 0x50, 0x4d, 0x50, 0x18, 0x0b, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x52, 0x03, 0x50, 0x4d, 0x50, 0x12, 0x2c, 0x0a, 0x03, 0x50, 0x43, 0x50, 0x18, 0x0c, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, - 0x50, 0x43, 0x50, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, - 0x44, 0x45, 0x52, 0x50, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x50, 0x72, 0x65, 0x66, - 0x65, 0x72, 0x72, 0x65, 0x64, 0x44, 0x45, 0x52, 0x50, 0x12, 0x53, 0x0a, 0x0d, 0x52, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, - 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x0d, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x59, - 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x34, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, - 0x79, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, - 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x34, 0x4c, 0x61, 0x74, 0x65, - 0x6e, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, - 0x56, 0x34, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x59, 0x0a, 0x0f, 0x52, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x56, 0x36, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x10, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, - 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x52, - 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x36, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x36, 0x4c, 0x61, 0x74, - 0x65, 0x6e, 0x63, 0x79, 0x12, 0x1a, 0x0a, 0x08, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x56, 0x34, - 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x56, 0x34, - 0x12, 0x1a, 0x0a, 0x08, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x56, 0x36, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x56, 0x36, 0x12, 0x40, 0x0a, 0x0d, - 0x43, 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x18, 0x13, 0x20, + 0x48, 0x41, 0x4b, 0x45, 0x10, 0x04, 0x22, 0xb2, 0x01, 0x0a, 0x08, 0x49, 0x50, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x38, 0x0a, + 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x22, 0x2e, 0x63, + 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, + 0x49, 0x50, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x2e, 0x49, 0x50, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x52, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x52, 0x0a, 0x07, 0x49, 0x50, 0x43, 0x6c, 0x61, + 0x73, 0x73, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x10, 0x00, 0x12, 0x0b, + 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, + 0x49, 0x4e, 0x4b, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x55, + 0x4e, 0x49, 0x51, 0x55, 0x45, 0x5f, 0x4c, 0x4f, 0x43, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0c, 0x0a, + 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x42, 0x41, 0x43, 0x4b, 0x10, 0x04, 0x22, 0xc4, 0x0a, 0x0a, 0x08, + 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x55, 0x44, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x49, 0x50, + 0x76, 0x36, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x49, 0x50, 0x76, 0x36, 0x12, 0x12, + 0x0a, 0x04, 0x49, 0x50, 0x76, 0x34, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x49, 0x50, + 0x76, 0x34, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x50, 0x76, 0x36, 0x43, 0x61, 0x6e, 0x53, 0x65, 0x6e, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x50, 0x76, 0x36, 0x43, 0x61, 0x6e, + 0x53, 0x65, 0x6e, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x49, 0x50, 0x76, 0x34, 0x43, 0x61, 0x6e, 0x53, + 0x65, 0x6e, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x49, 0x50, 0x76, 0x34, 0x43, + 0x61, 0x6e, 0x53, 0x65, 0x6e, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x4f, 0x53, 0x48, 0x61, 0x73, 0x49, + 0x50, 0x76, 0x36, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x4f, 0x53, 0x48, 0x61, 0x73, + 0x49, 0x50, 0x76, 0x36, 0x12, 0x16, 0x0a, 0x06, 0x49, 0x43, 0x4d, 0x50, 0x76, 0x34, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x49, 0x43, 0x4d, 0x50, 0x76, 0x34, 0x12, 0x50, 0x0a, 0x15, + 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x72, 0x69, 0x65, 0x73, 0x42, 0x79, 0x44, + 0x65, 0x73, 0x74, 0x49, 0x50, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, + 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x15, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, + 0x56, 0x61, 0x72, 0x69, 0x65, 0x73, 0x42, 0x79, 0x44, 0x65, 0x73, 0x74, 0x49, 0x50, 0x12, 0x3c, + 0x0a, 0x0b, 0x48, 0x61, 0x69, 0x72, 0x50, 0x69, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, - 0x0d, 0x43, 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, 0x1a, 0x5b, - 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x45, - 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x14, 0x52, - 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x34, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, 0x14, 0x52, 0x65, - 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x36, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xd7, 0x0c, 0x0a, 0x0e, 0x54, 0x65, - 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x04, - 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, - 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, - 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, - 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, - 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, - 0x31, 0x0a, 0x14, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x64, - 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, - 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, - 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x66, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x53, 0x65, - 0x6c, 0x66, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, - 0x49, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x70, 0x32, 0x70, 0x5f, - 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, + 0x0b, 0x48, 0x61, 0x69, 0x72, 0x50, 0x69, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x12, 0x2e, 0x0a, 0x04, + 0x55, 0x50, 0x6e, 0x50, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, + 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x04, 0x55, 0x50, 0x6e, 0x50, 0x12, 0x2c, 0x0a, 0x03, + 0x50, 0x4d, 0x50, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, + 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x50, 0x4d, 0x50, 0x12, 0x2c, 0x0a, 0x03, 0x50, 0x43, + 0x50, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x52, 0x03, 0x50, 0x43, 0x50, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x72, 0x65, 0x66, + 0x65, 0x72, 0x72, 0x65, 0x64, 0x44, 0x45, 0x52, 0x50, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0d, 0x50, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x65, 0x64, 0x44, 0x45, 0x52, 0x50, 0x12, 0x53, + 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, + 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, + 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x59, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x34, 0x4c, + 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, + 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, + 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, + 0x34, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x52, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x34, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x59, + 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x36, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, + 0x79, 0x18, 0x10, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, + 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x36, 0x4c, 0x61, 0x74, 0x65, + 0x6e, 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x56, 0x36, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x41, 0x0a, 0x08, 0x47, 0x6c, 0x6f, + 0x62, 0x61, 0x6c, 0x56, 0x34, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, + 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4e, + 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x49, 0x50, 0x52, 0x08, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x56, 0x34, 0x12, 0x41, 0x0a, 0x08, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x56, 0x36, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, - 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x2e, 0x50, 0x32, 0x50, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0b, 0x70, 0x32, - 0x70, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x55, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, - 0x5f, 0x69, 0x70, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, - 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x2e, 0x4c, 0x6f, 0x67, 0x49, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6c, 0x6f, 0x67, 0x49, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, 0x6d, 0x65, 0x5f, 0x64, 0x65, 0x72, 0x70, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x6d, 0x65, 0x44, 0x65, 0x72, 0x70, 0x12, 0x12, 0x0a, - 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x6f, 0x67, - 0x73, 0x12, 0x34, 0x0a, 0x08, 0x64, 0x65, 0x72, 0x70, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0d, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, - 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x52, 0x07, - 0x64, 0x65, 0x72, 0x70, 0x4d, 0x61, 0x70, 0x12, 0x43, 0x0a, 0x0f, 0x6c, 0x61, 0x74, 0x65, 0x73, - 0x74, 0x5f, 0x6e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, - 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0e, 0x6c, 0x61, - 0x74, 0x65, 0x73, 0x74, 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x40, 0x0a, 0x0e, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x0f, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x65, 0x12, 0x44, - 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x74, - 0x75, 0x70, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x32, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x2e, 0x4e, 0x65, 0x74, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x49, 0x50, 0x52, 0x08, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x56, 0x36, 0x12, + 0x40, 0x0a, 0x0d, 0x43, 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, 0x6c, + 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, + 0x75, 0x65, 0x52, 0x0d, 0x43, 0x61, 0x70, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x61, + 0x6c, 0x1a, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x4c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, - 0x65, 0x74, 0x75, 0x70, 0x12, 0x36, 0x0a, 0x09, 0x70, 0x32, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x75, - 0x70, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, + 0x0a, 0x14, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x34, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, + 0x79, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x08, 0x70, 0x32, 0x70, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x3c, 0x0a, 0x0c, - 0x64, 0x65, 0x72, 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x12, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, - 0x65, 0x72, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x3a, 0x0a, 0x0b, 0x70, 0x32, - 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x32, 0x70, 0x4c, - 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x46, 0x0a, 0x10, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, - 0x68, 0x70, 0x75, 0x74, 0x5f, 0x6d, 0x62, 0x69, 0x74, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x74, - 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x70, 0x75, 0x74, 0x4d, 0x62, 0x69, 0x74, 0x73, 0x1a, 0x64, - 0x0a, 0x08, 0x49, 0x50, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, - 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x76, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3e, 0x0a, 0x05, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, - 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x50, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x52, 0x05, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x1a, 0x78, 0x0a, 0x0b, 0x50, 0x32, 0x50, 0x45, 0x6e, 0x64, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x41, 0x0a, 0x06, 0x66, - 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, - 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, - 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x50, - 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x1a, 0x69, - 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x49, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x3f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, - 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x49, 0x50, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x05, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x29, 0x0a, 0x06, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x49, 0x53, 0x43, 0x4f, 0x4e, 0x4e, 0x45, 0x43, 0x54, - 0x45, 0x44, 0x10, 0x01, 0x22, 0x39, 0x0a, 0x0a, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x4c, 0x49, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, - 0x47, 0x45, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x44, 0x45, 0x52, 0x44, - 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x53, 0x50, 0x52, 0x4f, 0x58, 0x59, 0x10, 0x03, 0x22, - 0x52, 0x0a, 0x07, 0x49, 0x50, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x55, - 0x42, 0x4c, 0x49, 0x43, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, - 0x45, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x4c, 0x49, 0x4e, 0x4b, 0x5f, 0x4c, 0x4f, 0x43, 0x41, - 0x4c, 0x10, 0x02, 0x12, 0x10, 0x0a, 0x0c, 0x55, 0x4e, 0x49, 0x51, 0x55, 0x45, 0x5f, 0x4c, 0x4f, - 0x43, 0x41, 0x4c, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x4c, 0x4f, 0x4f, 0x50, 0x42, 0x41, 0x43, - 0x4b, 0x10, 0x04, 0x22, 0x4c, 0x0a, 0x10, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, + 0x6f, 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5d, 0x0a, + 0x14, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x56, 0x36, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x03, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x54, 0x0a, 0x0a, + 0x4e, 0x65, 0x74, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x49, 0x50, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, + 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x32, + 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, + 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, + 0x32, 0x2e, 0x49, 0x50, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x73, 0x22, 0xff, 0x0a, 0x0a, 0x0e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0c, 0x52, 0x02, 0x69, 0x64, 0x12, 0x2e, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, + 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x22, 0x13, 0x0a, 0x11, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x98, 0x02, 0x0a, 0x07, 0x54, 0x61, 0x69, 0x6c, 0x6e, - 0x65, 0x74, 0x12, 0x58, 0x0a, 0x0d, 0x50, 0x6f, 0x73, 0x74, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, - 0x74, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, - 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, + 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x31, 0x0a, 0x14, 0x64, 0x69, 0x73, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x4c, 0x0a, 0x0b, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, + 0x32, 0x2b, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, + 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x24, 0x0a, 0x0e, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x70, 0x32, 0x70, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, - 0x65, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, 0x0e, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x73, 0x12, 0x27, + 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x50, 0x32, 0x50, 0x45, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x52, 0x0b, 0x70, 0x32, 0x70, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x12, 0x55, 0x0a, 0x0d, 0x6c, 0x6f, 0x67, 0x5f, 0x69, 0x70, 0x5f, 0x68, 0x61, 0x73, + 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x63, 0x6f, 0x64, 0x65, + 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, + 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x4c, 0x6f, 0x67, 0x49, + 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x6c, 0x6f, + 0x67, 0x49, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x68, 0x6f, 0x6d, + 0x65, 0x5f, 0x64, 0x65, 0x72, 0x70, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, + 0x6d, 0x65, 0x44, 0x65, 0x72, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x18, 0x0c, + 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x08, 0x64, 0x65, + 0x72, 0x70, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x63, + 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, + 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x52, 0x07, 0x64, 0x65, 0x72, 0x70, 0x4d, 0x61, 0x70, + 0x12, 0x43, 0x0a, 0x0f, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6e, 0x65, 0x74, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64, 0x65, + 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x4e, 0x65, 0x74, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x52, 0x0e, 0x6c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x4e, 0x65, 0x74, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x12, 0x40, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x61, 0x67, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x41, 0x67, 0x65, 0x12, 0x44, 0x0a, 0x10, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x65, 0x74, 0x75, 0x70, 0x18, 0x10, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x36, 0x0a, + 0x09, 0x70, 0x32, 0x70, 0x5f, 0x73, 0x65, 0x74, 0x75, 0x70, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x70, 0x32, 0x70, + 0x53, 0x65, 0x74, 0x75, 0x70, 0x12, 0x3c, 0x0a, 0x0c, 0x64, 0x65, 0x72, 0x70, 0x5f, 0x6c, 0x61, + 0x74, 0x65, 0x6e, 0x63, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x64, 0x65, 0x72, 0x70, 0x4c, 0x61, 0x74, 0x65, + 0x6e, 0x63, 0x79, 0x12, 0x3a, 0x0a, 0x0b, 0x70, 0x32, 0x70, 0x5f, 0x6c, 0x61, 0x74, 0x65, 0x6e, + 0x63, 0x79, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x70, 0x32, 0x70, 0x4c, 0x61, 0x74, 0x65, 0x6e, 0x63, 0x79, 0x12, + 0x46, 0x0a, 0x10, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x70, 0x75, 0x74, 0x5f, 0x6d, 0x62, + 0x69, 0x74, 0x73, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x46, 0x6c, 0x6f, 0x61, + 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0f, 0x74, 0x68, 0x72, 0x6f, 0x75, 0x67, 0x68, 0x70, + 0x75, 0x74, 0x4d, 0x62, 0x69, 0x74, 0x73, 0x1a, 0x69, 0x0a, 0x0b, 0x50, 0x32, 0x50, 0x45, 0x6e, + 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x6f, + 0x72, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x32, + 0x0a, 0x06, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, - 0x32, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, - 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x45, 0x52, 0x50, 0x4d, - 0x61, 0x70, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x0a, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, - 0x74, 0x65, 0x12, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, - 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, - 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6f, 0x72, 0x64, - 0x69, 0x6e, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, 0x30, - 0x01, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x74, - 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x32, 0x2e, 0x49, 0x50, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x06, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x73, 0x1a, 0x5a, 0x0a, 0x10, 0x4c, 0x6f, 0x67, 0x49, 0x70, 0x48, 0x61, 0x73, 0x68, 0x65, + 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, + 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x49, 0x50, 0x46, 0x69, 0x65, + 0x6c, 0x64, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x29, + 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x43, 0x4f, 0x4e, 0x4e, + 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x49, 0x53, 0x43, 0x4f, + 0x4e, 0x4e, 0x45, 0x43, 0x54, 0x45, 0x44, 0x10, 0x01, 0x22, 0x39, 0x0a, 0x0a, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x4c, 0x49, 0x10, 0x00, + 0x12, 0x09, 0x0a, 0x05, 0x41, 0x47, 0x45, 0x4e, 0x54, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x43, + 0x4f, 0x44, 0x45, 0x52, 0x44, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x57, 0x53, 0x50, 0x52, 0x4f, + 0x58, 0x59, 0x10, 0x03, 0x22, 0x4c, 0x0a, 0x10, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, + 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x22, 0x13, 0x0a, 0x11, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x98, 0x02, 0x0a, 0x07, 0x54, 0x61, 0x69, 0x6c, + 0x6e, 0x65, 0x74, 0x12, 0x58, 0x0a, 0x0d, 0x50, 0x6f, 0x73, 0x74, 0x54, 0x65, 0x6c, 0x65, 0x6d, + 0x65, 0x74, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, + 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, + 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, + 0x6d, 0x65, 0x74, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x56, 0x0a, + 0x0e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, 0x73, 0x12, + 0x27, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, + 0x76, 0x32, 0x2e, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x44, 0x45, 0x52, 0x50, 0x4d, 0x61, 0x70, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, + 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x44, 0x45, 0x52, 0x50, + 0x4d, 0x61, 0x70, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x0a, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, + 0x61, 0x74, 0x65, 0x12, 0x23, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, + 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, + 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x43, 0x6f, 0x6f, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x28, 0x01, + 0x30, 0x01, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, + 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2094,102 +2157,106 @@ func file_tailnet_proto_tailnet_proto_rawDescGZIP() []byte { } var file_tailnet_proto_tailnet_proto_enumTypes = make([]protoimpl.EnumInfo, 4) -var file_tailnet_proto_tailnet_proto_msgTypes = make([]protoimpl.MessageInfo, 27) +var file_tailnet_proto_tailnet_proto_msgTypes = make([]protoimpl.MessageInfo, 28) var file_tailnet_proto_tailnet_proto_goTypes = []interface{}{ - (CoordinateResponse_PeerUpdate_Kind)(0), // 0: coder.tailnet.v2.CoordinateResponse.PeerUpdate.Kind - (TelemetryEvent_Status)(0), // 1: coder.tailnet.v2.TelemetryEvent.Status - (TelemetryEvent_ClientType)(0), // 2: coder.tailnet.v2.TelemetryEvent.ClientType - (TelemetryEvent_IPClass)(0), // 3: coder.tailnet.v2.TelemetryEvent.IPClass - (*DERPMap)(nil), // 4: coder.tailnet.v2.DERPMap - (*StreamDERPMapsRequest)(nil), // 5: coder.tailnet.v2.StreamDERPMapsRequest - (*Node)(nil), // 6: coder.tailnet.v2.Node - (*CoordinateRequest)(nil), // 7: coder.tailnet.v2.CoordinateRequest - (*CoordinateResponse)(nil), // 8: coder.tailnet.v2.CoordinateResponse - (*Netcheck)(nil), // 9: coder.tailnet.v2.Netcheck - (*TelemetryEvent)(nil), // 10: coder.tailnet.v2.TelemetryEvent - (*TelemetryRequest)(nil), // 11: coder.tailnet.v2.TelemetryRequest - (*TelemetryResponse)(nil), // 12: coder.tailnet.v2.TelemetryResponse - (*DERPMap_HomeParams)(nil), // 13: coder.tailnet.v2.DERPMap.HomeParams - (*DERPMap_Region)(nil), // 14: coder.tailnet.v2.DERPMap.Region - nil, // 15: coder.tailnet.v2.DERPMap.RegionsEntry - nil, // 16: coder.tailnet.v2.DERPMap.HomeParams.RegionScoreEntry - (*DERPMap_Region_Node)(nil), // 17: coder.tailnet.v2.DERPMap.Region.Node - nil, // 18: coder.tailnet.v2.Node.DerpLatencyEntry - nil, // 19: coder.tailnet.v2.Node.DerpForcedWebsocketEntry - (*CoordinateRequest_UpdateSelf)(nil), // 20: coder.tailnet.v2.CoordinateRequest.UpdateSelf - (*CoordinateRequest_Disconnect)(nil), // 21: coder.tailnet.v2.CoordinateRequest.Disconnect - (*CoordinateRequest_Tunnel)(nil), // 22: coder.tailnet.v2.CoordinateRequest.Tunnel - (*CoordinateRequest_ReadyForHandshake)(nil), // 23: coder.tailnet.v2.CoordinateRequest.ReadyForHandshake - (*CoordinateResponse_PeerUpdate)(nil), // 24: coder.tailnet.v2.CoordinateResponse.PeerUpdate - nil, // 25: coder.tailnet.v2.Netcheck.RegionLatencyEntry - nil, // 26: coder.tailnet.v2.Netcheck.RegionV4LatencyEntry - nil, // 27: coder.tailnet.v2.Netcheck.RegionV6LatencyEntry - (*TelemetryEvent_IPFields)(nil), // 28: coder.tailnet.v2.TelemetryEvent.IPFields - (*TelemetryEvent_P2PEndpoint)(nil), // 29: coder.tailnet.v2.TelemetryEvent.P2PEndpoint - nil, // 30: coder.tailnet.v2.TelemetryEvent.LogIpHashesEntry - (*timestamppb.Timestamp)(nil), // 31: google.protobuf.Timestamp - (*wrapperspb.BoolValue)(nil), // 32: google.protobuf.BoolValue - (*durationpb.Duration)(nil), // 33: google.protobuf.Duration - (*wrapperspb.FloatValue)(nil), // 34: google.protobuf.FloatValue + (CoordinateResponse_PeerUpdate_Kind)(0), // 0: coder.tailnet.v2.CoordinateResponse.PeerUpdate.Kind + (IPFields_IPClass)(0), // 1: coder.tailnet.v2.IPFields.IPClass + (TelemetryEvent_Status)(0), // 2: coder.tailnet.v2.TelemetryEvent.Status + (TelemetryEvent_ClientType)(0), // 3: coder.tailnet.v2.TelemetryEvent.ClientType + (*DERPMap)(nil), // 4: coder.tailnet.v2.DERPMap + (*StreamDERPMapsRequest)(nil), // 5: coder.tailnet.v2.StreamDERPMapsRequest + (*Node)(nil), // 6: coder.tailnet.v2.Node + (*CoordinateRequest)(nil), // 7: coder.tailnet.v2.CoordinateRequest + (*CoordinateResponse)(nil), // 8: coder.tailnet.v2.CoordinateResponse + (*IPFields)(nil), // 9: coder.tailnet.v2.IPFields + (*Netcheck)(nil), // 10: coder.tailnet.v2.Netcheck + (*TelemetryEvent)(nil), // 11: coder.tailnet.v2.TelemetryEvent + (*TelemetryRequest)(nil), // 12: coder.tailnet.v2.TelemetryRequest + (*TelemetryResponse)(nil), // 13: coder.tailnet.v2.TelemetryResponse + (*DERPMap_HomeParams)(nil), // 14: coder.tailnet.v2.DERPMap.HomeParams + (*DERPMap_Region)(nil), // 15: coder.tailnet.v2.DERPMap.Region + nil, // 16: coder.tailnet.v2.DERPMap.RegionsEntry + nil, // 17: coder.tailnet.v2.DERPMap.HomeParams.RegionScoreEntry + (*DERPMap_Region_Node)(nil), // 18: coder.tailnet.v2.DERPMap.Region.Node + nil, // 19: coder.tailnet.v2.Node.DerpLatencyEntry + nil, // 20: coder.tailnet.v2.Node.DerpForcedWebsocketEntry + (*CoordinateRequest_UpdateSelf)(nil), // 21: coder.tailnet.v2.CoordinateRequest.UpdateSelf + (*CoordinateRequest_Disconnect)(nil), // 22: coder.tailnet.v2.CoordinateRequest.Disconnect + (*CoordinateRequest_Tunnel)(nil), // 23: coder.tailnet.v2.CoordinateRequest.Tunnel + (*CoordinateRequest_ReadyForHandshake)(nil), // 24: coder.tailnet.v2.CoordinateRequest.ReadyForHandshake + (*CoordinateResponse_PeerUpdate)(nil), // 25: coder.tailnet.v2.CoordinateResponse.PeerUpdate + nil, // 26: coder.tailnet.v2.Netcheck.RegionLatencyEntry + nil, // 27: coder.tailnet.v2.Netcheck.RegionV4LatencyEntry + nil, // 28: coder.tailnet.v2.Netcheck.RegionV6LatencyEntry + (*Netcheck_NetcheckIP)(nil), // 29: coder.tailnet.v2.Netcheck.NetcheckIP + (*TelemetryEvent_P2PEndpoint)(nil), // 30: coder.tailnet.v2.TelemetryEvent.P2PEndpoint + nil, // 31: coder.tailnet.v2.TelemetryEvent.LogIpHashesEntry + (*timestamppb.Timestamp)(nil), // 32: google.protobuf.Timestamp + (*wrapperspb.BoolValue)(nil), // 33: google.protobuf.BoolValue + (*durationpb.Duration)(nil), // 34: google.protobuf.Duration + (*wrapperspb.FloatValue)(nil), // 35: google.protobuf.FloatValue } var file_tailnet_proto_tailnet_proto_depIdxs = []int32{ - 13, // 0: coder.tailnet.v2.DERPMap.home_params:type_name -> coder.tailnet.v2.DERPMap.HomeParams - 15, // 1: coder.tailnet.v2.DERPMap.regions:type_name -> coder.tailnet.v2.DERPMap.RegionsEntry - 31, // 2: coder.tailnet.v2.Node.as_of:type_name -> google.protobuf.Timestamp - 18, // 3: coder.tailnet.v2.Node.derp_latency:type_name -> coder.tailnet.v2.Node.DerpLatencyEntry - 19, // 4: coder.tailnet.v2.Node.derp_forced_websocket:type_name -> coder.tailnet.v2.Node.DerpForcedWebsocketEntry - 20, // 5: coder.tailnet.v2.CoordinateRequest.update_self:type_name -> coder.tailnet.v2.CoordinateRequest.UpdateSelf - 21, // 6: coder.tailnet.v2.CoordinateRequest.disconnect:type_name -> coder.tailnet.v2.CoordinateRequest.Disconnect - 22, // 7: coder.tailnet.v2.CoordinateRequest.add_tunnel:type_name -> coder.tailnet.v2.CoordinateRequest.Tunnel - 22, // 8: coder.tailnet.v2.CoordinateRequest.remove_tunnel:type_name -> coder.tailnet.v2.CoordinateRequest.Tunnel - 23, // 9: coder.tailnet.v2.CoordinateRequest.ready_for_handshake:type_name -> coder.tailnet.v2.CoordinateRequest.ReadyForHandshake - 24, // 10: coder.tailnet.v2.CoordinateResponse.peer_updates:type_name -> coder.tailnet.v2.CoordinateResponse.PeerUpdate - 32, // 11: coder.tailnet.v2.Netcheck.MappingVariesByDestIP:type_name -> google.protobuf.BoolValue - 32, // 12: coder.tailnet.v2.Netcheck.HairPinning:type_name -> google.protobuf.BoolValue - 32, // 13: coder.tailnet.v2.Netcheck.UPnP:type_name -> google.protobuf.BoolValue - 32, // 14: coder.tailnet.v2.Netcheck.PMP:type_name -> google.protobuf.BoolValue - 32, // 15: coder.tailnet.v2.Netcheck.PCP:type_name -> google.protobuf.BoolValue - 25, // 16: coder.tailnet.v2.Netcheck.RegionLatency:type_name -> coder.tailnet.v2.Netcheck.RegionLatencyEntry - 26, // 17: coder.tailnet.v2.Netcheck.RegionV4Latency:type_name -> coder.tailnet.v2.Netcheck.RegionV4LatencyEntry - 27, // 18: coder.tailnet.v2.Netcheck.RegionV6Latency:type_name -> coder.tailnet.v2.Netcheck.RegionV6LatencyEntry - 32, // 19: coder.tailnet.v2.Netcheck.CaptivePortal:type_name -> google.protobuf.BoolValue - 31, // 20: coder.tailnet.v2.TelemetryEvent.time:type_name -> google.protobuf.Timestamp - 1, // 21: coder.tailnet.v2.TelemetryEvent.status:type_name -> coder.tailnet.v2.TelemetryEvent.Status - 2, // 22: coder.tailnet.v2.TelemetryEvent.client_type:type_name -> coder.tailnet.v2.TelemetryEvent.ClientType - 29, // 23: coder.tailnet.v2.TelemetryEvent.p2p_endpoint:type_name -> coder.tailnet.v2.TelemetryEvent.P2PEndpoint - 30, // 24: coder.tailnet.v2.TelemetryEvent.log_ip_hashes:type_name -> coder.tailnet.v2.TelemetryEvent.LogIpHashesEntry - 4, // 25: coder.tailnet.v2.TelemetryEvent.derp_map:type_name -> coder.tailnet.v2.DERPMap - 9, // 26: coder.tailnet.v2.TelemetryEvent.latest_netcheck:type_name -> coder.tailnet.v2.Netcheck - 33, // 27: coder.tailnet.v2.TelemetryEvent.connection_age:type_name -> google.protobuf.Duration - 33, // 28: coder.tailnet.v2.TelemetryEvent.connection_setup:type_name -> google.protobuf.Duration - 33, // 29: coder.tailnet.v2.TelemetryEvent.p2p_setup:type_name -> google.protobuf.Duration - 33, // 30: coder.tailnet.v2.TelemetryEvent.derp_latency:type_name -> google.protobuf.Duration - 33, // 31: coder.tailnet.v2.TelemetryEvent.p2p_latency:type_name -> google.protobuf.Duration - 34, // 32: coder.tailnet.v2.TelemetryEvent.throughput_mbits:type_name -> google.protobuf.FloatValue - 10, // 33: coder.tailnet.v2.TelemetryRequest.events:type_name -> coder.tailnet.v2.TelemetryEvent - 16, // 34: coder.tailnet.v2.DERPMap.HomeParams.region_score:type_name -> coder.tailnet.v2.DERPMap.HomeParams.RegionScoreEntry - 17, // 35: coder.tailnet.v2.DERPMap.Region.nodes:type_name -> coder.tailnet.v2.DERPMap.Region.Node - 14, // 36: coder.tailnet.v2.DERPMap.RegionsEntry.value:type_name -> coder.tailnet.v2.DERPMap.Region - 6, // 37: coder.tailnet.v2.CoordinateRequest.UpdateSelf.node:type_name -> coder.tailnet.v2.Node - 6, // 38: coder.tailnet.v2.CoordinateResponse.PeerUpdate.node:type_name -> coder.tailnet.v2.Node - 0, // 39: coder.tailnet.v2.CoordinateResponse.PeerUpdate.kind:type_name -> coder.tailnet.v2.CoordinateResponse.PeerUpdate.Kind - 33, // 40: coder.tailnet.v2.Netcheck.RegionLatencyEntry.value:type_name -> google.protobuf.Duration - 33, // 41: coder.tailnet.v2.Netcheck.RegionV4LatencyEntry.value:type_name -> google.protobuf.Duration - 33, // 42: coder.tailnet.v2.Netcheck.RegionV6LatencyEntry.value:type_name -> google.protobuf.Duration - 3, // 43: coder.tailnet.v2.TelemetryEvent.IPFields.class:type_name -> coder.tailnet.v2.TelemetryEvent.IPClass - 28, // 44: coder.tailnet.v2.TelemetryEvent.P2PEndpoint.fields:type_name -> coder.tailnet.v2.TelemetryEvent.IPFields - 28, // 45: coder.tailnet.v2.TelemetryEvent.LogIpHashesEntry.value:type_name -> coder.tailnet.v2.TelemetryEvent.IPFields - 11, // 46: coder.tailnet.v2.Tailnet.PostTelemetry:input_type -> coder.tailnet.v2.TelemetryRequest - 5, // 47: coder.tailnet.v2.Tailnet.StreamDERPMaps:input_type -> coder.tailnet.v2.StreamDERPMapsRequest - 7, // 48: coder.tailnet.v2.Tailnet.Coordinate:input_type -> coder.tailnet.v2.CoordinateRequest - 12, // 49: coder.tailnet.v2.Tailnet.PostTelemetry:output_type -> coder.tailnet.v2.TelemetryResponse - 4, // 50: coder.tailnet.v2.Tailnet.StreamDERPMaps:output_type -> coder.tailnet.v2.DERPMap - 8, // 51: coder.tailnet.v2.Tailnet.Coordinate:output_type -> coder.tailnet.v2.CoordinateResponse - 49, // [49:52] is the sub-list for method output_type - 46, // [46:49] is the sub-list for method input_type - 46, // [46:46] is the sub-list for extension type_name - 46, // [46:46] is the sub-list for extension extendee - 0, // [0:46] is the sub-list for field type_name + 14, // 0: coder.tailnet.v2.DERPMap.home_params:type_name -> coder.tailnet.v2.DERPMap.HomeParams + 16, // 1: coder.tailnet.v2.DERPMap.regions:type_name -> coder.tailnet.v2.DERPMap.RegionsEntry + 32, // 2: coder.tailnet.v2.Node.as_of:type_name -> google.protobuf.Timestamp + 19, // 3: coder.tailnet.v2.Node.derp_latency:type_name -> coder.tailnet.v2.Node.DerpLatencyEntry + 20, // 4: coder.tailnet.v2.Node.derp_forced_websocket:type_name -> coder.tailnet.v2.Node.DerpForcedWebsocketEntry + 21, // 5: coder.tailnet.v2.CoordinateRequest.update_self:type_name -> coder.tailnet.v2.CoordinateRequest.UpdateSelf + 22, // 6: coder.tailnet.v2.CoordinateRequest.disconnect:type_name -> coder.tailnet.v2.CoordinateRequest.Disconnect + 23, // 7: coder.tailnet.v2.CoordinateRequest.add_tunnel:type_name -> coder.tailnet.v2.CoordinateRequest.Tunnel + 23, // 8: coder.tailnet.v2.CoordinateRequest.remove_tunnel:type_name -> coder.tailnet.v2.CoordinateRequest.Tunnel + 24, // 9: coder.tailnet.v2.CoordinateRequest.ready_for_handshake:type_name -> coder.tailnet.v2.CoordinateRequest.ReadyForHandshake + 25, // 10: coder.tailnet.v2.CoordinateResponse.peer_updates:type_name -> coder.tailnet.v2.CoordinateResponse.PeerUpdate + 1, // 11: coder.tailnet.v2.IPFields.class:type_name -> coder.tailnet.v2.IPFields.IPClass + 33, // 12: coder.tailnet.v2.Netcheck.MappingVariesByDestIP:type_name -> google.protobuf.BoolValue + 33, // 13: coder.tailnet.v2.Netcheck.HairPinning:type_name -> google.protobuf.BoolValue + 33, // 14: coder.tailnet.v2.Netcheck.UPnP:type_name -> google.protobuf.BoolValue + 33, // 15: coder.tailnet.v2.Netcheck.PMP:type_name -> google.protobuf.BoolValue + 33, // 16: coder.tailnet.v2.Netcheck.PCP:type_name -> google.protobuf.BoolValue + 26, // 17: coder.tailnet.v2.Netcheck.RegionLatency:type_name -> coder.tailnet.v2.Netcheck.RegionLatencyEntry + 27, // 18: coder.tailnet.v2.Netcheck.RegionV4Latency:type_name -> coder.tailnet.v2.Netcheck.RegionV4LatencyEntry + 28, // 19: coder.tailnet.v2.Netcheck.RegionV6Latency:type_name -> coder.tailnet.v2.Netcheck.RegionV6LatencyEntry + 29, // 20: coder.tailnet.v2.Netcheck.GlobalV4:type_name -> coder.tailnet.v2.Netcheck.NetcheckIP + 29, // 21: coder.tailnet.v2.Netcheck.GlobalV6:type_name -> coder.tailnet.v2.Netcheck.NetcheckIP + 33, // 22: coder.tailnet.v2.Netcheck.CaptivePortal:type_name -> google.protobuf.BoolValue + 32, // 23: coder.tailnet.v2.TelemetryEvent.time:type_name -> google.protobuf.Timestamp + 2, // 24: coder.tailnet.v2.TelemetryEvent.status:type_name -> coder.tailnet.v2.TelemetryEvent.Status + 3, // 25: coder.tailnet.v2.TelemetryEvent.client_type:type_name -> coder.tailnet.v2.TelemetryEvent.ClientType + 30, // 26: coder.tailnet.v2.TelemetryEvent.p2p_endpoint:type_name -> coder.tailnet.v2.TelemetryEvent.P2PEndpoint + 31, // 27: coder.tailnet.v2.TelemetryEvent.log_ip_hashes:type_name -> coder.tailnet.v2.TelemetryEvent.LogIpHashesEntry + 4, // 28: coder.tailnet.v2.TelemetryEvent.derp_map:type_name -> coder.tailnet.v2.DERPMap + 10, // 29: coder.tailnet.v2.TelemetryEvent.latest_netcheck:type_name -> coder.tailnet.v2.Netcheck + 34, // 30: coder.tailnet.v2.TelemetryEvent.connection_age:type_name -> google.protobuf.Duration + 34, // 31: coder.tailnet.v2.TelemetryEvent.connection_setup:type_name -> google.protobuf.Duration + 34, // 32: coder.tailnet.v2.TelemetryEvent.p2p_setup:type_name -> google.protobuf.Duration + 34, // 33: coder.tailnet.v2.TelemetryEvent.derp_latency:type_name -> google.protobuf.Duration + 34, // 34: coder.tailnet.v2.TelemetryEvent.p2p_latency:type_name -> google.protobuf.Duration + 35, // 35: coder.tailnet.v2.TelemetryEvent.throughput_mbits:type_name -> google.protobuf.FloatValue + 11, // 36: coder.tailnet.v2.TelemetryRequest.events:type_name -> coder.tailnet.v2.TelemetryEvent + 17, // 37: coder.tailnet.v2.DERPMap.HomeParams.region_score:type_name -> coder.tailnet.v2.DERPMap.HomeParams.RegionScoreEntry + 18, // 38: coder.tailnet.v2.DERPMap.Region.nodes:type_name -> coder.tailnet.v2.DERPMap.Region.Node + 15, // 39: coder.tailnet.v2.DERPMap.RegionsEntry.value:type_name -> coder.tailnet.v2.DERPMap.Region + 6, // 40: coder.tailnet.v2.CoordinateRequest.UpdateSelf.node:type_name -> coder.tailnet.v2.Node + 6, // 41: coder.tailnet.v2.CoordinateResponse.PeerUpdate.node:type_name -> coder.tailnet.v2.Node + 0, // 42: coder.tailnet.v2.CoordinateResponse.PeerUpdate.kind:type_name -> coder.tailnet.v2.CoordinateResponse.PeerUpdate.Kind + 34, // 43: coder.tailnet.v2.Netcheck.RegionLatencyEntry.value:type_name -> google.protobuf.Duration + 34, // 44: coder.tailnet.v2.Netcheck.RegionV4LatencyEntry.value:type_name -> google.protobuf.Duration + 34, // 45: coder.tailnet.v2.Netcheck.RegionV6LatencyEntry.value:type_name -> google.protobuf.Duration + 9, // 46: coder.tailnet.v2.Netcheck.NetcheckIP.fields:type_name -> coder.tailnet.v2.IPFields + 9, // 47: coder.tailnet.v2.TelemetryEvent.P2PEndpoint.fields:type_name -> coder.tailnet.v2.IPFields + 9, // 48: coder.tailnet.v2.TelemetryEvent.LogIpHashesEntry.value:type_name -> coder.tailnet.v2.IPFields + 12, // 49: coder.tailnet.v2.Tailnet.PostTelemetry:input_type -> coder.tailnet.v2.TelemetryRequest + 5, // 50: coder.tailnet.v2.Tailnet.StreamDERPMaps:input_type -> coder.tailnet.v2.StreamDERPMapsRequest + 7, // 51: coder.tailnet.v2.Tailnet.Coordinate:input_type -> coder.tailnet.v2.CoordinateRequest + 13, // 52: coder.tailnet.v2.Tailnet.PostTelemetry:output_type -> coder.tailnet.v2.TelemetryResponse + 4, // 53: coder.tailnet.v2.Tailnet.StreamDERPMaps:output_type -> coder.tailnet.v2.DERPMap + 8, // 54: coder.tailnet.v2.Tailnet.Coordinate:output_type -> coder.tailnet.v2.CoordinateResponse + 52, // [52:55] is the sub-list for method output_type + 49, // [49:52] is the sub-list for method input_type + 49, // [49:49] is the sub-list for extension type_name + 49, // [49:49] is the sub-list for extension extendee + 0, // [0:49] is the sub-list for field type_name } func init() { file_tailnet_proto_tailnet_proto_init() } @@ -2259,7 +2326,7 @@ func file_tailnet_proto_tailnet_proto_init() { } } file_tailnet_proto_tailnet_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Netcheck); i { + switch v := v.(*IPFields); i { case 0: return &v.state case 1: @@ -2271,7 +2338,7 @@ func file_tailnet_proto_tailnet_proto_init() { } } file_tailnet_proto_tailnet_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryEvent); i { + switch v := v.(*Netcheck); i { case 0: return &v.state case 1: @@ -2283,7 +2350,7 @@ func file_tailnet_proto_tailnet_proto_init() { } } file_tailnet_proto_tailnet_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryRequest); i { + switch v := v.(*TelemetryEvent); i { case 0: return &v.state case 1: @@ -2295,7 +2362,7 @@ func file_tailnet_proto_tailnet_proto_init() { } } file_tailnet_proto_tailnet_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryResponse); i { + switch v := v.(*TelemetryRequest); i { case 0: return &v.state case 1: @@ -2307,7 +2374,7 @@ func file_tailnet_proto_tailnet_proto_init() { } } file_tailnet_proto_tailnet_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DERPMap_HomeParams); i { + switch v := v.(*TelemetryResponse); i { case 0: return &v.state case 1: @@ -2319,6 +2386,18 @@ func file_tailnet_proto_tailnet_proto_init() { } } file_tailnet_proto_tailnet_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DERPMap_HomeParams); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_tailnet_proto_tailnet_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DERPMap_Region); i { case 0: return &v.state @@ -2330,7 +2409,7 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + file_tailnet_proto_tailnet_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DERPMap_Region_Node); i { case 0: return &v.state @@ -2342,7 +2421,7 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_tailnet_proto_tailnet_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CoordinateRequest_UpdateSelf); i { case 0: return &v.state @@ -2354,7 +2433,7 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_tailnet_proto_tailnet_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CoordinateRequest_Disconnect); i { case 0: return &v.state @@ -2366,7 +2445,7 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_tailnet_proto_tailnet_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CoordinateRequest_Tunnel); i { case 0: return &v.state @@ -2378,7 +2457,7 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + file_tailnet_proto_tailnet_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CoordinateRequest_ReadyForHandshake); i { case 0: return &v.state @@ -2390,7 +2469,7 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} { + file_tailnet_proto_tailnet_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*CoordinateResponse_PeerUpdate); i { case 0: return &v.state @@ -2402,8 +2481,8 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TelemetryEvent_IPFields); i { + file_tailnet_proto_tailnet_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Netcheck_NetcheckIP); i { case 0: return &v.state case 1: @@ -2414,7 +2493,7 @@ func file_tailnet_proto_tailnet_proto_init() { return nil } } - file_tailnet_proto_tailnet_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + file_tailnet_proto_tailnet_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TelemetryEvent_P2PEndpoint); i { case 0: return &v.state @@ -2433,7 +2512,7 @@ func file_tailnet_proto_tailnet_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_tailnet_proto_tailnet_proto_rawDesc, NumEnums: 4, - NumMessages: 27, + NumMessages: 28, NumExtensions: 0, NumServices: 1, }, diff --git a/tailnet/proto/tailnet.proto b/tailnet/proto/tailnet.proto index cba3d83929d24..b4e4b50bbe992 100644 --- a/tailnet/proto/tailnet.proto +++ b/tailnet/proto/tailnet.proto @@ -102,6 +102,18 @@ message CoordinateResponse { string error = 2; } +message IPFields { + int32 version = 1; + enum IPClass { + PUBLIC = 0; + PRIVATE = 1; + LINK_LOCAL = 2; + UNIQUE_LOCAL = 3; + LOOPBACK = 4; + } + IPClass class = 2; +} + message Netcheck { bool UDP = 1; bool IPv6 = 2; @@ -123,8 +135,12 @@ message Netcheck { map RegionV4Latency = 15; map RegionV6Latency = 16; - string GlobalV4 = 17; - string GlobalV6 = 18; + message NetcheckIP { + string hash = 1; + IPFields fields = 2; + } + NetcheckIP GlobalV4 = 17; + NetcheckIP GlobalV6 = 18; google.protobuf.BoolValue CaptivePortal = 19; } @@ -142,19 +158,6 @@ message TelemetryEvent { WSPROXY = 3; } - enum IPClass { - PUBLIC = 0; - PRIVATE = 1; - LINK_LOCAL = 2; - UNIQUE_LOCAL = 3; - LOOPBACK = 4; - } - - message IPFields { - int32 version = 1; - IPClass class = 2; - } - message P2PEndpoint { string hash = 1; int32 port = 2; @@ -167,8 +170,8 @@ message TelemetryEvent { Status status = 4; string disconnection_reason = 5; ClientType client_type = 6; - string node_id_self = 7; - string node_id_remote = 8; + bytes node_id_self = 7; + bytes node_id_remote = 8; P2PEndpoint p2p_endpoint = 9; map log_ip_hashes = 10; string home_derp = 11; diff --git a/tailnet/service.go b/tailnet/service.go index 870b7ff7a4a67..d118950c91447 100644 --- a/tailnet/service.go +++ b/tailnet/service.go @@ -4,18 +4,19 @@ import ( "context" "io" "net" + "sync" "sync/atomic" "time" "github.com/google/uuid" "github.com/hashicorp/yamux" - "storj.io/drpc/drpcerr" "storj.io/drpc/drpcmux" "storj.io/drpc/drpcserver" "tailscale.com/tailcfg" "cdr.dev/slog" "github.com/coder/coder/v2/apiversion" + "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/tailnet/proto" "golang.org/x/xerrors" @@ -118,14 +119,94 @@ func (s ClientService) ServeConnV2(ctx context.Context, conn net.Conn, streamID // DRPCService is the dRPC-based, version 2.x of the tailnet API and implements proto.DRPCClientServer type DRPCService struct { - CoordPtr *atomic.Pointer[Coordinator] - Logger slog.Logger - DerpMapUpdateFrequency time.Duration - DerpMapFn func() *tailcfg.DERPMap + CoordPtr *atomic.Pointer[Coordinator] + Logger slog.Logger + DerpMapUpdateFrequency time.Duration + DerpMapFn func() *tailcfg.DERPMap + NetworkTelemetryBatchFrequency time.Duration + NetworkTelemetryBatchMaxSize int + NetworkTelemetryBatchFn func(batch []telemetry.NetworkEvent) + + mu sync.Mutex + pendingNetworkEvents []telemetry.NetworkEvent +} + +func (s *DRPCService) writeTelemetryEvents(events []telemetry.NetworkEvent) { + if s.NetworkTelemetryBatchFn == nil { + return + } + s.NetworkTelemetryBatchFn(events) } -func (*DRPCService) PostTelemetry(context.Context, *proto.TelemetryRequest) (*proto.TelemetryResponse, error) { - return nil, drpcerr.WithCode(xerrors.New("Unimplemented"), drpcerr.Unimplemented) +func (s *DRPCService) sendTelemetryBatch() { + s.mu.Lock() + defer s.mu.Unlock() + events := s.pendingNetworkEvents + s.pendingNetworkEvents = []telemetry.NetworkEvent{} + go s.writeTelemetryEvents(events) +} + +// PeriodicTelemetryBatcher starts a goroutine to periodically send telemetry +// events to the telemetry backend. The returned function is a cleanup function +// that should be called when the service is no longer needed. +// +// Note: calling the returned function does not unblock any in-flight calls to +// the underlying telemetry backend that come from PostTelemetry due to +// s.TelemetryBatchMaxSize. +func (s *DRPCService) PeriodicTelemetryBatcher() func() { + var ( + closed = make(chan struct{}) + done = make(chan struct{}) + ) + if s.NetworkTelemetryBatchFn == nil { + return func() {} + } + + go func() { + defer close(done) + ticker := time.NewTicker(s.NetworkTelemetryBatchFrequency) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + s.sendTelemetryBatch() + case <-closed: + // Send any remaining telemetry events before exiting. + s.sendTelemetryBatch() + return + } + } + }() + + return func() { + close(closed) + <-done + } +} + +func (s *DRPCService) PostTelemetry(_ context.Context, req *proto.TelemetryRequest) (*proto.TelemetryResponse, error) { + s.mu.Lock() + defer s.mu.Unlock() + + for _, pEvent := range req.Events { + tEvent, err := telemetry.NetworkEventFromProto(pEvent) + if err != nil { + // TODO(@deansheather): log? return an error? + continue + } + + s.pendingNetworkEvents = append(s.pendingNetworkEvents, tEvent) + + if len(s.pendingNetworkEvents) >= s.NetworkTelemetryBatchMaxSize { + events := s.pendingNetworkEvents + s.pendingNetworkEvents = []telemetry.NetworkEvent{} + // Perform the send in a goroutine to avoid blocking the DRPC call. + go s.writeTelemetryEvents(events) + } + } + + return &proto.TelemetryResponse{}, nil } func (s *DRPCService) StreamDERPMaps(_ *proto.StreamDERPMapsRequest, stream proto.DRPCTailnet_StreamDERPMapsStream) error { From 776f0430a07b28aff663db2e743ff78624e67f77 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Thu, 27 Jun 2024 12:04:12 +0000 Subject: [PATCH 2/6] Progress --- coderd/agentapi/api.go | 2 - coderd/agentapi/api_test.go | 106 ++++++++++++++++++++++++++++++++++ coderd/telemetry/telemetry.go | 16 ++--- coderd/workspaceagentsrpc.go | 2 +- tailnet/proto/tailnet.pb.go | 16 ++--- tailnet/proto/tailnet.proto | 4 +- 6 files changed, 121 insertions(+), 25 deletions(-) create mode 100644 coderd/agentapi/api_test.go diff --git a/coderd/agentapi/api.go b/coderd/agentapi/api.go index 37c84a966f4cb..f99731cb78ddf 100644 --- a/coderd/agentapi/api.go +++ b/coderd/agentapi/api.go @@ -22,7 +22,6 @@ import ( "github.com/coder/coder/v2/coderd/database/pubsub" "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/prometheusmetrics" - "github.com/coder/coder/v2/coderd/schedule" "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/coderd/workspacestats" @@ -62,7 +61,6 @@ type Options struct { Pubsub pubsub.Pubsub DerpMapFn func() *tailcfg.DERPMap TailnetCoordinator *atomic.Pointer[tailnet.Coordinator] - TemplateScheduleStore *atomic.Pointer[schedule.TemplateScheduleStore] StatsReporter *workspacestats.Reporter AppearanceFetcher *atomic.Pointer[appearance.Fetcher] PublishWorkspaceUpdateFn func(ctx context.Context, workspaceID uuid.UUID) diff --git a/coderd/agentapi/api_test.go b/coderd/agentapi/api_test.go new file mode 100644 index 0000000000000..98b1e072d65bf --- /dev/null +++ b/coderd/agentapi/api_test.go @@ -0,0 +1,106 @@ +package agentapi_test + +import ( + "context" + "net/url" + "sync/atomic" + "testing" + "time" + + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "go.uber.org/goleak" + "tailscale.com/tailcfg" + + "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/agent/proto" + "github.com/coder/coder/v2/coderd/agentapi" + "github.com/coder/coder/v2/coderd/appearance" + "github.com/coder/coder/v2/coderd/database/dbtestutil" + "github.com/coder/coder/v2/coderd/externalauth" + "github.com/coder/coder/v2/coderd/prometheusmetrics" + "github.com/coder/coder/v2/coderd/schedule" + "github.com/coder/coder/v2/coderd/telemetry" + "github.com/coder/coder/v2/coderd/workspacestats" + "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/tailnet" + "github.com/coder/coder/v2/tailnet/tailnettest" + "github.com/coder/coder/v2/testutil" +) + +func TestMain(m *testing.M) { + goleak.VerifyTestMain(m) +} + +func Test_APIClose(t *testing.T) { + t.Parallel() + + ctx := testutil.Context(t, testutil.WaitMedium) + log := slogtest.Make(t, nil) + + db, pubsub := dbtestutil.NewDB(t) + fCoord := tailnettest.NewFakeCoordinator() + var coord tailnet.Coordinator = fCoord + coordPtr := atomic.Pointer[tailnet.Coordinator]{} + coordPtr.Store(&coord) + + mockTemplateScheduleStore := schedule.MockTemplateScheduleStore{} + var templateScheduleStore schedule.TemplateScheduleStore = mockTemplateScheduleStore + templateScheduleStorePtr := atomic.Pointer[schedule.TemplateScheduleStore]{} + templateScheduleStorePtr.Store(&templateScheduleStore) + + statsBatcher, closeBatcher, err := workspacestats.NewBatcher(ctx, workspacestats.BatcherWithStore(db)) + require.NoError(t, err) + t.Cleanup(closeBatcher) + statsTracker := workspacestats.NewTracker(db) + t.Cleanup(func() { + _ = statsTracker.Close() + }) + statsReporter := workspacestats.NewReporter(workspacestats.ReporterOptions{ + Database: db, + Logger: log, + Pubsub: pubsub, + TemplateScheduleStore: &templateScheduleStorePtr, + StatsBatcher: statsBatcher, + UsageTracker: statsTracker, + UpdateAgentMetricsFn: func(_ context.Context, _ prometheusmetrics.AgentMetricLabels, _ []*proto.Stats_Metric) {}, + AppStatBatchSize: 0, + }) + + appearanceFetcherPtr := atomic.Pointer[appearance.Fetcher]{} + appearanceFetcherPtr.Store(&appearance.DefaultFetcher) + + api := agentapi.New(agentapi.Options{ + AgentID: uuid.New(), + Ctx: ctx, + Log: log, + Database: db, + Pubsub: pubsub, + DerpMapFn: func() *tailcfg.DERPMap { + return &tailcfg.DERPMap{Regions: map[int]*tailcfg.DERPRegion{999: {RegionCode: "test"}}} + }, + TailnetCoordinator: &coordPtr, + StatsReporter: statsReporter, + AppearanceFetcher: &appearanceFetcherPtr, + PublishWorkspaceUpdateFn: func(_ context.Context, _ uuid.UUID) {}, + NetworkTelemetryBatchFn: func(_ []telemetry.NetworkEvent) {}, + AccessURL: &url.URL{ + Scheme: "http", + Host: "localhost", + }, + AppHostname: "", + AgentStatsRefreshInterval: time.Second, + DisableDirectConnections: false, + DerpForceWebSockets: false, + DerpMapUpdateFrequency: time.Second, + NetworkTelemetryBatchFrequency: time.Second, + NetworkTelemetryBatchMaxSize: 1, + ExternalAuthConfigs: []*externalauth.Config{}, + Experiments: codersdk.Experiments{}, + WorkspaceID: uuid.New(), + UpdateAgentMetricsFn: func(_ context.Context, _ prometheusmetrics.AgentMetricLabels, _ []*proto.Stats_Metric) {}, + }) + + err = api.Close() + require.NoError(t, err) +} diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index d2dfbaba44204..e35475c4add71 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -1240,8 +1240,8 @@ type NetworkEvent struct { Status string `json:"status"` // connected, disconnected DisconnectionReason string `json:"disconnection_reason"` ClientType string `json:"client_type"` // cli, agent, coderd, wsproxy - NodeIDSelf uuid.UUID `json:"node_id_self"` - NodeIDRemote uuid.UUID `json:"node_id_remote"` + NodeIDSelf uint64 `json:"node_id_self"` + NodeIDRemote uint64 `json:"node_id_remote"` P2PEndpoint NetworkEventP2PEndpoint `json:"p2p_endpoint"` LogIPHashes map[string]NetworkEventIPFields `json:"log_ip_hashes"` HomeDERP string `json:"home_derp"` @@ -1272,14 +1272,6 @@ func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, er if err != nil { return NetworkEvent{}, xerrors.Errorf("parse id %q: %w", proto.Id, err) } - nodeIDSelf, err := uuid.ParseBytes(proto.NodeIdSelf) - if err != nil { - return NetworkEvent{}, xerrors.Errorf("parse node_id_self %q: %w", proto.NodeIdSelf, err) - } - nodeIDRemote, err := uuid.ParseBytes(proto.NodeIdRemote) - if err != nil { - return NetworkEvent{}, xerrors.Errorf("parse node_id_remote %q: %w", proto.NodeIdRemote, err) - } logIPHashes := make(map[string]NetworkEventIPFields, len(proto.LogIpHashes)) for k, v := range proto.LogIpHashes { @@ -1293,8 +1285,8 @@ func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, er Status: strings.ToLower(proto.Status.String()), DisconnectionReason: proto.DisconnectionReason, ClientType: strings.ToLower(proto.ClientType.String()), - NodeIDSelf: nodeIDSelf, - NodeIDRemote: nodeIDRemote, + NodeIDSelf: proto.NodeIdSelf, + NodeIDRemote: proto.NodeIdRemote, P2PEndpoint: p2pEndpointFromProto(proto.P2PEndpoint), LogIPHashes: logIPHashes, HomeDERP: proto.HomeDerp, diff --git a/coderd/workspaceagentsrpc.go b/coderd/workspaceagentsrpc.go index 9853d170eff7c..efa187bc14e98 100644 --- a/coderd/workspaceagentsrpc.go +++ b/coderd/workspaceagentsrpc.go @@ -131,7 +131,6 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { Pubsub: api.Pubsub, DerpMapFn: api.DERPMap, TailnetCoordinator: &api.TailnetCoordinator, - TemplateScheduleStore: api.TemplateScheduleStore, AppearanceFetcher: &api.AppearanceFetcher, StatsReporter: api.statsReporter, PublishWorkspaceUpdateFn: api.publishWorkspaceUpdate, @@ -157,6 +156,7 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { WorkspaceID: build.WorkspaceID, // saves the extra lookup later UpdateAgentMetricsFn: api.UpdateAgentMetrics, }) + defer agentAPI.Close() streamID := tailnet.StreamID{ Name: fmt.Sprintf("%s-%s-%s", owner.Username, workspace.Name, workspaceAgent.Name), diff --git a/tailnet/proto/tailnet.pb.go b/tailnet/proto/tailnet.pb.go index f293b7ba69f55..a9268f04d992c 100644 --- a/tailnet/proto/tailnet.pb.go +++ b/tailnet/proto/tailnet.pb.go @@ -835,8 +835,8 @@ type TelemetryEvent struct { Status TelemetryEvent_Status `protobuf:"varint,4,opt,name=status,proto3,enum=coder.tailnet.v2.TelemetryEvent_Status" json:"status,omitempty"` DisconnectionReason string `protobuf:"bytes,5,opt,name=disconnection_reason,json=disconnectionReason,proto3" json:"disconnection_reason,omitempty"` ClientType TelemetryEvent_ClientType `protobuf:"varint,6,opt,name=client_type,json=clientType,proto3,enum=coder.tailnet.v2.TelemetryEvent_ClientType" json:"client_type,omitempty"` - NodeIdSelf []byte `protobuf:"bytes,7,opt,name=node_id_self,json=nodeIdSelf,proto3" json:"node_id_self,omitempty"` - NodeIdRemote []byte `protobuf:"bytes,8,opt,name=node_id_remote,json=nodeIdRemote,proto3" json:"node_id_remote,omitempty"` + NodeIdSelf uint64 `protobuf:"varint,7,opt,name=node_id_self,json=nodeIdSelf,proto3" json:"node_id_self,omitempty"` + NodeIdRemote uint64 `protobuf:"varint,8,opt,name=node_id_remote,json=nodeIdRemote,proto3" json:"node_id_remote,omitempty"` P2PEndpoint *TelemetryEvent_P2PEndpoint `protobuf:"bytes,9,opt,name=p2p_endpoint,json=p2pEndpoint,proto3" json:"p2p_endpoint,omitempty"` LogIpHashes map[string]*IPFields `protobuf:"bytes,10,rep,name=log_ip_hashes,json=logIpHashes,proto3" json:"log_ip_hashes,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` HomeDerp string `protobuf:"bytes,11,opt,name=home_derp,json=homeDerp,proto3" json:"home_derp,omitempty"` @@ -925,18 +925,18 @@ func (x *TelemetryEvent) GetClientType() TelemetryEvent_ClientType { return TelemetryEvent_CLI } -func (x *TelemetryEvent) GetNodeIdSelf() []byte { +func (x *TelemetryEvent) GetNodeIdSelf() uint64 { if x != nil { return x.NodeIdSelf } - return nil + return 0 } -func (x *TelemetryEvent) GetNodeIdRemote() []byte { +func (x *TelemetryEvent) GetNodeIdRemote() uint64 { if x != nil { return x.NodeIdRemote } - return nil + return 0 } func (x *TelemetryEvent) GetP2PEndpoint() *TelemetryEvent_P2PEndpoint { @@ -2046,10 +2046,10 @@ var file_tailnet_proto_tailnet_proto_rawDesc = []byte{ 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, + 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x73, 0x65, 0x6c, 0x66, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x53, 0x65, 0x6c, 0x66, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x18, 0x08, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x01, 0x28, 0x04, 0x52, 0x0c, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x4f, 0x0a, 0x0c, 0x70, 0x32, 0x70, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2e, 0x74, 0x61, 0x69, 0x6c, 0x6e, 0x65, 0x74, 0x2e, 0x76, 0x32, 0x2e, 0x54, 0x65, 0x6c, 0x65, 0x6d, diff --git a/tailnet/proto/tailnet.proto b/tailnet/proto/tailnet.proto index b4e4b50bbe992..b8e97d8a7a493 100644 --- a/tailnet/proto/tailnet.proto +++ b/tailnet/proto/tailnet.proto @@ -170,8 +170,8 @@ message TelemetryEvent { Status status = 4; string disconnection_reason = 5; ClientType client_type = 6; - bytes node_id_self = 7; - bytes node_id_remote = 8; + uint64 node_id_self = 7; + uint64 node_id_remote = 8; P2PEndpoint p2p_endpoint = 9; map log_ip_hashes = 10; string home_derp = 11; From 2148b6263388b9740472851257b1af248a9d3947 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Thu, 27 Jun 2024 13:28:07 +0000 Subject: [PATCH 3/6] Avoid slim database import --- coderd/agentapi/api.go | 3 +-- coderd/agentapi/api_test.go | 4 ++-- coderd/workspaceagentsrpc.go | 15 +++++++++++-- tailnet/service.go | 42 ++++++++++++++++++++---------------- 4 files changed, 39 insertions(+), 25 deletions(-) diff --git a/coderd/agentapi/api.go b/coderd/agentapi/api.go index f99731cb78ddf..a5626c29d68b2 100644 --- a/coderd/agentapi/api.go +++ b/coderd/agentapi/api.go @@ -22,7 +22,6 @@ import ( "github.com/coder/coder/v2/coderd/database/pubsub" "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/prometheusmetrics" - "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/coderd/workspacestats" "github.com/coder/coder/v2/codersdk" @@ -65,7 +64,7 @@ type Options struct { AppearanceFetcher *atomic.Pointer[appearance.Fetcher] PublishWorkspaceUpdateFn func(ctx context.Context, workspaceID uuid.UUID) PublishWorkspaceAgentLogsUpdateFn func(ctx context.Context, workspaceAgentID uuid.UUID, msg agentsdk.LogsNotifyMessage) - NetworkTelemetryBatchFn func(batch []telemetry.NetworkEvent) + NetworkTelemetryBatchFn func(batch []*tailnetproto.TelemetryEvent) AccessURL *url.URL AppHostname string diff --git a/coderd/agentapi/api_test.go b/coderd/agentapi/api_test.go index 98b1e072d65bf..2a14c4d735e73 100644 --- a/coderd/agentapi/api_test.go +++ b/coderd/agentapi/api_test.go @@ -20,10 +20,10 @@ import ( "github.com/coder/coder/v2/coderd/externalauth" "github.com/coder/coder/v2/coderd/prometheusmetrics" "github.com/coder/coder/v2/coderd/schedule" - "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/coderd/workspacestats" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/tailnet" + tailnetproto "github.com/coder/coder/v2/tailnet/proto" "github.com/coder/coder/v2/tailnet/tailnettest" "github.com/coder/coder/v2/testutil" ) @@ -83,7 +83,7 @@ func Test_APIClose(t *testing.T) { StatsReporter: statsReporter, AppearanceFetcher: &appearanceFetcherPtr, PublishWorkspaceUpdateFn: func(_ context.Context, _ uuid.UUID) {}, - NetworkTelemetryBatchFn: func(_ []telemetry.NetworkEvent) {}, + NetworkTelemetryBatchFn: func(_ []*tailnetproto.TelemetryEvent) {}, AccessURL: &url.URL{ Scheme: "http", Host: "localhost", diff --git a/coderd/workspaceagentsrpc.go b/coderd/workspaceagentsrpc.go index efa187bc14e98..27fac31a7f01f 100644 --- a/coderd/workspaceagentsrpc.go +++ b/coderd/workspaceagentsrpc.go @@ -28,6 +28,7 @@ import ( "github.com/coder/coder/v2/coderd/util/ptr" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/tailnet" + tailnetproto "github.com/coder/coder/v2/tailnet/proto" ) // @Summary Workspace agent RPC API @@ -135,9 +136,19 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { StatsReporter: api.statsReporter, PublishWorkspaceUpdateFn: api.publishWorkspaceUpdate, PublishWorkspaceAgentLogsUpdateFn: api.publishWorkspaceAgentLogsUpdate, - NetworkTelemetryBatchFn: func(batch []telemetry.NetworkEvent) { + NetworkTelemetryBatchFn: func(batch []*tailnetproto.TelemetryEvent) { + telemetryEvents := make([]telemetry.NetworkEvent, 0, len(batch)) + for _, pEvent := range batch { + tEvent, err := telemetry.NetworkEventFromProto(pEvent) + if err != nil { + // Events that fail to be converted get discarded for now. + continue + } + telemetryEvents = append(telemetryEvents, tEvent) + } + api.Telemetry.Report(&telemetry.Snapshot{ - NetworkEvents: batch, + NetworkEvents: telemetryEvents, }) }, diff --git a/tailnet/service.go b/tailnet/service.go index d118950c91447..a7b9de911456b 100644 --- a/tailnet/service.go +++ b/tailnet/service.go @@ -10,16 +10,14 @@ import ( "github.com/google/uuid" "github.com/hashicorp/yamux" + "golang.org/x/xerrors" "storj.io/drpc/drpcmux" "storj.io/drpc/drpcserver" "tailscale.com/tailcfg" "cdr.dev/slog" "github.com/coder/coder/v2/apiversion" - "github.com/coder/coder/v2/coderd/telemetry" "github.com/coder/coder/v2/tailnet/proto" - - "golang.org/x/xerrors" ) type streamIDContextKey struct{} @@ -125,13 +123,14 @@ type DRPCService struct { DerpMapFn func() *tailcfg.DERPMap NetworkTelemetryBatchFrequency time.Duration NetworkTelemetryBatchMaxSize int - NetworkTelemetryBatchFn func(batch []telemetry.NetworkEvent) + NetworkTelemetryBatchFn func(batch []*proto.TelemetryEvent) - mu sync.Mutex - pendingNetworkEvents []telemetry.NetworkEvent + mu sync.Mutex + networkEventBatchTicker *time.Ticker + pendingNetworkEvents []*proto.TelemetryEvent } -func (s *DRPCService) writeTelemetryEvents(events []telemetry.NetworkEvent) { +func (s *DRPCService) writeTelemetryEvents(events []*proto.TelemetryEvent) { if s.NetworkTelemetryBatchFn == nil { return } @@ -142,13 +141,14 @@ func (s *DRPCService) sendTelemetryBatch() { s.mu.Lock() defer s.mu.Unlock() events := s.pendingNetworkEvents - s.pendingNetworkEvents = []telemetry.NetworkEvent{} + s.pendingNetworkEvents = []*proto.TelemetryEvent{} go s.writeTelemetryEvents(events) } // PeriodicTelemetryBatcher starts a goroutine to periodically send telemetry // events to the telemetry backend. The returned function is a cleanup function -// that should be called when the service is no longer needed. +// that should be called when the service is no longer needed. Calling this more +// than once will panic. // // Note: calling the returned function does not unblock any in-flight calls to // the underlying telemetry backend that come from PostTelemetry due to @@ -162,9 +162,16 @@ func (s *DRPCService) PeriodicTelemetryBatcher() func() { return func() {} } + s.mu.Lock() + defer s.mu.Unlock() + if s.networkEventBatchTicker != nil { + panic("PeriodicTelemetryBatcher called more than once") + } + ticker := time.NewTicker(s.NetworkTelemetryBatchFrequency) + s.networkEventBatchTicker = ticker + go func() { defer close(done) - ticker := time.NewTicker(s.NetworkTelemetryBatchFrequency) defer ticker.Stop() for { @@ -189,19 +196,16 @@ func (s *DRPCService) PostTelemetry(_ context.Context, req *proto.TelemetryReque s.mu.Lock() defer s.mu.Unlock() - for _, pEvent := range req.Events { - tEvent, err := telemetry.NetworkEventFromProto(pEvent) - if err != nil { - // TODO(@deansheather): log? return an error? - continue - } - - s.pendingNetworkEvents = append(s.pendingNetworkEvents, tEvent) + for _, event := range req.Events { + s.pendingNetworkEvents = append(s.pendingNetworkEvents, event) if len(s.pendingNetworkEvents) >= s.NetworkTelemetryBatchMaxSize { events := s.pendingNetworkEvents - s.pendingNetworkEvents = []telemetry.NetworkEvent{} + s.pendingNetworkEvents = []*proto.TelemetryEvent{} // Perform the send in a goroutine to avoid blocking the DRPC call. + if s.networkEventBatchTicker != nil { + s.networkEventBatchTicker.Reset(s.NetworkTelemetryBatchFrequency) + } go s.writeTelemetryEvents(events) } } From c3123494513ad3b8361eac5f6032df3e3625c0d6 Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Thu, 27 Jun 2024 14:43:03 +0000 Subject: [PATCH 4/6] Refactor network telemetry batching --- coderd/agentapi/api.go | 41 ++-- coderd/agentapi/api_test.go | 106 --------- coderd/coderd.go | 18 +- coderd/workspaceagentsrpc.go | 51 +++-- .../workspacesdk/connector_internal_test.go | 11 +- enterprise/coderd/coderd.go | 13 +- enterprise/tailnet/workspaceproxy.go | 19 +- .../wsproxy/wsproxysdk/wsproxysdk_test.go | 12 +- tailnet/coordinator_test.go | 24 ++- tailnet/service.go | 204 +++++++++--------- tailnet/service_test.go | 22 +- tailnet/test/integration/integration.go | 17 +- 12 files changed, 228 insertions(+), 310 deletions(-) delete mode 100644 coderd/agentapi/api_test.go diff --git a/coderd/agentapi/api.go b/coderd/agentapi/api.go index a5626c29d68b2..7aeb3a7de9d78 100644 --- a/coderd/agentapi/api.go +++ b/coderd/agentapi/api.go @@ -46,7 +46,6 @@ type API struct { mu sync.Mutex cachedWorkspaceID uuid.UUID - drpcServiceClose func() } var _ agentproto.DRPCAgentServer = &API{} @@ -64,18 +63,16 @@ type Options struct { AppearanceFetcher *atomic.Pointer[appearance.Fetcher] PublishWorkspaceUpdateFn func(ctx context.Context, workspaceID uuid.UUID) PublishWorkspaceAgentLogsUpdateFn func(ctx context.Context, workspaceAgentID uuid.UUID, msg agentsdk.LogsNotifyMessage) - NetworkTelemetryBatchFn func(batch []*tailnetproto.TelemetryEvent) - - AccessURL *url.URL - AppHostname string - AgentStatsRefreshInterval time.Duration - DisableDirectConnections bool - DerpForceWebSockets bool - DerpMapUpdateFrequency time.Duration - NetworkTelemetryBatchFrequency time.Duration - NetworkTelemetryBatchMaxSize int - ExternalAuthConfigs []*externalauth.Config - Experiments codersdk.Experiments + NetworkTelemetryHandler func(batch []*tailnetproto.TelemetryEvent) + + AccessURL *url.URL + AppHostname string + AgentStatsRefreshInterval time.Duration + DisableDirectConnections bool + DerpForceWebSockets bool + DerpMapUpdateFrequency time.Duration + ExternalAuthConfigs []*externalauth.Config + Experiments codersdk.Experiments // Optional: // WorkspaceID avoids a future lookup to find the workspace ID by setting @@ -156,24 +153,16 @@ func New(opts Options) *API { } api.DRPCService = &tailnet.DRPCService{ - CoordPtr: opts.TailnetCoordinator, - Logger: opts.Log, - DerpMapUpdateFrequency: opts.DerpMapUpdateFrequency, - DerpMapFn: opts.DerpMapFn, - NetworkTelemetryBatchFrequency: opts.NetworkTelemetryBatchFrequency, - NetworkTelemetryBatchMaxSize: opts.NetworkTelemetryBatchMaxSize, - NetworkTelemetryBatchFn: opts.NetworkTelemetryBatchFn, + CoordPtr: opts.TailnetCoordinator, + Logger: opts.Log, + DerpMapUpdateFrequency: opts.DerpMapUpdateFrequency, + DerpMapFn: opts.DerpMapFn, + NetworkTelemetryHandler: opts.NetworkTelemetryHandler, } - api.drpcServiceClose = api.DRPCService.PeriodicTelemetryBatcher() return api } -func (a *API) Close() error { - a.drpcServiceClose() - return nil -} - func (a *API) Server(ctx context.Context) (*drpcserver.Server, error) { mux := drpcmux.New() err := agentproto.DRPCRegisterAgent(mux, a) diff --git a/coderd/agentapi/api_test.go b/coderd/agentapi/api_test.go deleted file mode 100644 index 2a14c4d735e73..0000000000000 --- a/coderd/agentapi/api_test.go +++ /dev/null @@ -1,106 +0,0 @@ -package agentapi_test - -import ( - "context" - "net/url" - "sync/atomic" - "testing" - "time" - - "github.com/google/uuid" - "github.com/stretchr/testify/require" - "go.uber.org/goleak" - "tailscale.com/tailcfg" - - "cdr.dev/slog/sloggers/slogtest" - "github.com/coder/coder/v2/agent/proto" - "github.com/coder/coder/v2/coderd/agentapi" - "github.com/coder/coder/v2/coderd/appearance" - "github.com/coder/coder/v2/coderd/database/dbtestutil" - "github.com/coder/coder/v2/coderd/externalauth" - "github.com/coder/coder/v2/coderd/prometheusmetrics" - "github.com/coder/coder/v2/coderd/schedule" - "github.com/coder/coder/v2/coderd/workspacestats" - "github.com/coder/coder/v2/codersdk" - "github.com/coder/coder/v2/tailnet" - tailnetproto "github.com/coder/coder/v2/tailnet/proto" - "github.com/coder/coder/v2/tailnet/tailnettest" - "github.com/coder/coder/v2/testutil" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} - -func Test_APIClose(t *testing.T) { - t.Parallel() - - ctx := testutil.Context(t, testutil.WaitMedium) - log := slogtest.Make(t, nil) - - db, pubsub := dbtestutil.NewDB(t) - fCoord := tailnettest.NewFakeCoordinator() - var coord tailnet.Coordinator = fCoord - coordPtr := atomic.Pointer[tailnet.Coordinator]{} - coordPtr.Store(&coord) - - mockTemplateScheduleStore := schedule.MockTemplateScheduleStore{} - var templateScheduleStore schedule.TemplateScheduleStore = mockTemplateScheduleStore - templateScheduleStorePtr := atomic.Pointer[schedule.TemplateScheduleStore]{} - templateScheduleStorePtr.Store(&templateScheduleStore) - - statsBatcher, closeBatcher, err := workspacestats.NewBatcher(ctx, workspacestats.BatcherWithStore(db)) - require.NoError(t, err) - t.Cleanup(closeBatcher) - statsTracker := workspacestats.NewTracker(db) - t.Cleanup(func() { - _ = statsTracker.Close() - }) - statsReporter := workspacestats.NewReporter(workspacestats.ReporterOptions{ - Database: db, - Logger: log, - Pubsub: pubsub, - TemplateScheduleStore: &templateScheduleStorePtr, - StatsBatcher: statsBatcher, - UsageTracker: statsTracker, - UpdateAgentMetricsFn: func(_ context.Context, _ prometheusmetrics.AgentMetricLabels, _ []*proto.Stats_Metric) {}, - AppStatBatchSize: 0, - }) - - appearanceFetcherPtr := atomic.Pointer[appearance.Fetcher]{} - appearanceFetcherPtr.Store(&appearance.DefaultFetcher) - - api := agentapi.New(agentapi.Options{ - AgentID: uuid.New(), - Ctx: ctx, - Log: log, - Database: db, - Pubsub: pubsub, - DerpMapFn: func() *tailcfg.DERPMap { - return &tailcfg.DERPMap{Regions: map[int]*tailcfg.DERPRegion{999: {RegionCode: "test"}}} - }, - TailnetCoordinator: &coordPtr, - StatsReporter: statsReporter, - AppearanceFetcher: &appearanceFetcherPtr, - PublishWorkspaceUpdateFn: func(_ context.Context, _ uuid.UUID) {}, - NetworkTelemetryBatchFn: func(_ []*tailnetproto.TelemetryEvent) {}, - AccessURL: &url.URL{ - Scheme: "http", - Host: "localhost", - }, - AppHostname: "", - AgentStatsRefreshInterval: time.Second, - DisableDirectConnections: false, - DerpForceWebSockets: false, - DerpMapUpdateFrequency: time.Second, - NetworkTelemetryBatchFrequency: time.Second, - NetworkTelemetryBatchMaxSize: 1, - ExternalAuthConfigs: []*externalauth.Config{}, - Experiments: codersdk.Experiments{}, - WorkspaceID: uuid.New(), - UpdateAgentMetricsFn: func(_ context.Context, _ prometheusmetrics.AgentMetricLabels, _ []*proto.Stats_Metric) {}, - }) - - err = api.Close() - require.NoError(t, err) -} diff --git a/coderd/coderd.go b/coderd/coderd.go index 292b0cb171574..02761d13a3646 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -547,12 +547,18 @@ func New(options *Options) *API { if options.DeploymentValues.Prometheus.Enable { options.PrometheusRegistry.MustRegister(stn) } - api.TailnetClientService, err = tailnet.NewClientService( - api.Logger.Named("tailnetclient"), - &api.TailnetCoordinator, - api.Options.DERPMapUpdateFrequency, - api.DERPMap, + api.NetworkTelemetryBatcher = tailnet.NewNetworkTelemetryBatcher( + api.Options.NetworkTelemetryBatchFrequency, + api.Options.NetworkTelemetryBatchMaxSize, + api.handleNetworkTelemetry, ) + api.TailnetClientService, err = tailnet.NewClientService(tailnet.ClientServiceOptions{ + Logger: api.Logger.Named("tailnetclient"), + CoordPtr: &api.TailnetCoordinator, + DERPMapUpdateFrequency: api.Options.DERPMapUpdateFrequency, + DERPMapFn: api.DERPMap, + NetworkTelemetryHandler: api.NetworkTelemetryBatcher.Handler, + }) if err != nil { api.Logger.Fatal(api.ctx, "failed to initialize tailnet client service", slog.Error(err)) } @@ -1263,6 +1269,7 @@ type API struct { Auditor atomic.Pointer[audit.Auditor] WorkspaceClientCoordinateOverride atomic.Pointer[func(rw http.ResponseWriter) bool] TailnetCoordinator atomic.Pointer[tailnet.Coordinator] + NetworkTelemetryBatcher *tailnet.NetworkTelemetryBatcher TailnetClientService *tailnet.ClientService QuotaCommitter atomic.Pointer[proto.QuotaCommitter] AppearanceFetcher atomic.Pointer[appearance.Fetcher] @@ -1356,6 +1363,7 @@ func (api *API) Close() error { } _ = api.agentProvider.Close() _ = api.statsReporter.Close() + _ = api.NetworkTelemetryBatcher.Close() return nil } diff --git a/coderd/workspaceagentsrpc.go b/coderd/workspaceagentsrpc.go index 27fac31a7f01f..cd37349f25634 100644 --- a/coderd/workspaceagentsrpc.go +++ b/coderd/workspaceagentsrpc.go @@ -136,38 +136,21 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { StatsReporter: api.statsReporter, PublishWorkspaceUpdateFn: api.publishWorkspaceUpdate, PublishWorkspaceAgentLogsUpdateFn: api.publishWorkspaceAgentLogsUpdate, - NetworkTelemetryBatchFn: func(batch []*tailnetproto.TelemetryEvent) { - telemetryEvents := make([]telemetry.NetworkEvent, 0, len(batch)) - for _, pEvent := range batch { - tEvent, err := telemetry.NetworkEventFromProto(pEvent) - if err != nil { - // Events that fail to be converted get discarded for now. - continue - } - telemetryEvents = append(telemetryEvents, tEvent) - } - - api.Telemetry.Report(&telemetry.Snapshot{ - NetworkEvents: telemetryEvents, - }) - }, + NetworkTelemetryHandler: api.NetworkTelemetryBatcher.Handler, - AccessURL: api.AccessURL, - AppHostname: api.AppHostname, - AgentStatsRefreshInterval: api.AgentStatsRefreshInterval, - DisableDirectConnections: api.DeploymentValues.DERP.Config.BlockDirect.Value(), - DerpForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(), - DerpMapUpdateFrequency: api.Options.DERPMapUpdateFrequency, - NetworkTelemetryBatchFrequency: api.Options.NetworkTelemetryBatchFrequency, - NetworkTelemetryBatchMaxSize: api.Options.NetworkTelemetryBatchMaxSize, - ExternalAuthConfigs: api.ExternalAuthConfigs, - Experiments: api.Experiments, + AccessURL: api.AccessURL, + AppHostname: api.AppHostname, + AgentStatsRefreshInterval: api.AgentStatsRefreshInterval, + DisableDirectConnections: api.DeploymentValues.DERP.Config.BlockDirect.Value(), + DerpForceWebSockets: api.DeploymentValues.DERP.Config.ForceWebSockets.Value(), + DerpMapUpdateFrequency: api.Options.DERPMapUpdateFrequency, + ExternalAuthConfigs: api.ExternalAuthConfigs, + Experiments: api.Experiments, // Optional: WorkspaceID: build.WorkspaceID, // saves the extra lookup later UpdateAgentMetricsFn: api.UpdateAgentMetrics, }) - defer agentAPI.Close() streamID := tailnet.StreamID{ Name: fmt.Sprintf("%s-%s-%s", owner.Username, workspace.Name, workspaceAgent.Name), @@ -184,6 +167,22 @@ func (api *API) workspaceAgentRPC(rw http.ResponseWriter, r *http.Request) { } } +func (api *API) handleNetworkTelemetry(batch []*tailnetproto.TelemetryEvent) { + telemetryEvents := make([]telemetry.NetworkEvent, 0, len(batch)) + for _, pEvent := range batch { + tEvent, err := telemetry.NetworkEventFromProto(pEvent) + if err != nil { + // Events that fail to be converted get discarded for now. + continue + } + telemetryEvents = append(telemetryEvents, tEvent) + } + + api.Telemetry.Report(&telemetry.Snapshot{ + NetworkEvents: telemetryEvents, + }) +} + type yamuxPingerCloser struct { mux *yamux.Session } diff --git a/codersdk/workspacesdk/connector_internal_test.go b/codersdk/workspacesdk/connector_internal_test.go index c7fc036ffa2a1..2e5716ee17870 100644 --- a/codersdk/workspacesdk/connector_internal_test.go +++ b/codersdk/workspacesdk/connector_internal_test.go @@ -50,10 +50,13 @@ func TestTailnetAPIConnector_Disconnects(t *testing.T) { coordPtr.Store(&coord) derpMapCh := make(chan *tailcfg.DERPMap) defer close(derpMapCh) - svc, err := tailnet.NewClientService( - logger, &coordPtr, - time.Millisecond, func() *tailcfg.DERPMap { return <-derpMapCh }, - ) + svc, err := tailnet.NewClientService(tailnet.ClientServiceOptions{ + Logger: logger, + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: time.Millisecond, + DERPMapFn: func() *tailcfg.DERPMap { return <-derpMapCh }, + NetworkTelemetryHandler: func(batch []*proto.TelemetryEvent) {}, + }) require.NoError(t, err) svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/enterprise/coderd/coderd.go b/enterprise/coderd/coderd.go index 743bd628d8630..cfdfbddb79940 100644 --- a/enterprise/coderd/coderd.go +++ b/enterprise/coderd/coderd.go @@ -138,12 +138,13 @@ func New(ctx context.Context, options *Options) (_ *API, err error) { } return api.fetchRegions(ctx) } - api.tailnetService, err = tailnet.NewClientService( - api.Logger.Named("tailnetclient"), - &api.AGPL.TailnetCoordinator, - api.Options.DERPMapUpdateFrequency, - api.AGPL.DERPMap, - ) + api.tailnetService, err = tailnet.NewClientService(agpltailnet.ClientServiceOptions{ + Logger: api.Logger.Named("tailnetclient"), + CoordPtr: &api.AGPL.TailnetCoordinator, + DERPMapUpdateFrequency: api.Options.DERPMapUpdateFrequency, + DERPMapFn: api.AGPL.DERPMap, + NetworkTelemetryHandler: api.AGPL.NetworkTelemetryBatcher.Handler, + }) if err != nil { api.Logger.Fatal(api.ctx, "failed to initialize tailnet client service", slog.Error(err)) } diff --git a/enterprise/tailnet/workspaceproxy.go b/enterprise/tailnet/workspaceproxy.go index 22cc0b3e73b6e..674536755434f 100644 --- a/enterprise/tailnet/workspaceproxy.go +++ b/enterprise/tailnet/workspaceproxy.go @@ -6,12 +6,10 @@ import ( "encoding/json" "errors" "net" - "sync/atomic" "time" "github.com/google/uuid" "golang.org/x/xerrors" - "tailscale.com/tailcfg" "cdr.dev/slog" "github.com/coder/coder/v2/apiversion" @@ -25,15 +23,14 @@ type ClientService struct { // NewClientService returns a ClientService based on the given Coordinator pointer. The pointer is // loaded on each processed connection. -func NewClientService( - logger slog.Logger, - coordPtr *atomic.Pointer[agpl.Coordinator], - derpMapUpdateFrequency time.Duration, - derpMapFn func() *tailcfg.DERPMap, -) ( - *ClientService, error, -) { - s, err := agpl.NewClientService(logger, coordPtr, derpMapUpdateFrequency, derpMapFn) +func NewClientService(options agpl.ClientServiceOptions) (*ClientService, error) { + s, err := agpl.NewClientService(agpl.ClientServiceOptions{ + Logger: options.Logger, + CoordPtr: options.CoordPtr, + DERPMapUpdateFrequency: options.DERPMapUpdateFrequency, + DERPMapFn: options.DERPMapFn, + NetworkTelemetryHandler: options.NetworkTelemetryHandler, + }) if err != nil { return nil, err } diff --git a/enterprise/wsproxy/wsproxysdk/wsproxysdk_test.go b/enterprise/wsproxy/wsproxysdk/wsproxysdk_test.go index c94b712cc9872..1ed49881092fb 100644 --- a/enterprise/wsproxy/wsproxysdk/wsproxysdk_test.go +++ b/enterprise/wsproxy/wsproxysdk/wsproxysdk_test.go @@ -171,11 +171,13 @@ func TestDialCoordinator(t *testing.T) { coordPtr := atomic.Pointer[agpl.Coordinator]{} coordPtr.Store(&coord) - cSrv, err := tailnet.NewClientService( - logger, &coordPtr, - time.Hour, - func() *tailcfg.DERPMap { panic("not implemented") }, - ) + cSrv, err := tailnet.NewClientService(agpl.ClientServiceOptions{ + Logger: logger, + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: time.Hour, + DERPMapFn: func() *tailcfg.DERPMap { panic("not implemented") }, + NetworkTelemetryHandler: func(batch []*proto.TelemetryEvent) { panic("not implemented") }, + }) require.NoError(t, err) // buffer the channels here, so we don't need to read and write in goroutines to diff --git a/tailnet/coordinator_test.go b/tailnet/coordinator_test.go index ddf5006614645..cdf288c98ddb9 100644 --- a/tailnet/coordinator_test.go +++ b/tailnet/coordinator_test.go @@ -624,11 +624,13 @@ func TestRemoteCoordination(t *testing.T) { var coord tailnet.Coordinator = mCoord coordPtr := atomic.Pointer[tailnet.Coordinator]{} coordPtr.Store(&coord) - svc, err := tailnet.NewClientService( - logger.Named("svc"), &coordPtr, - time.Hour, - func() *tailcfg.DERPMap { panic("not implemented") }, - ) + svc, err := tailnet.NewClientService(tailnet.ClientServiceOptions{ + Logger: logger.Named("svc"), + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: time.Hour, + DERPMapFn: func() *tailcfg.DERPMap { panic("not implemented") }, + NetworkTelemetryHandler: func(batch []*proto.TelemetryEvent) { panic("not implemented") }, + }) require.NoError(t, err) sC, cC := net.Pipe() @@ -673,11 +675,13 @@ func TestRemoteCoordination_SendsReadyForHandshake(t *testing.T) { var coord tailnet.Coordinator = mCoord coordPtr := atomic.Pointer[tailnet.Coordinator]{} coordPtr.Store(&coord) - svc, err := tailnet.NewClientService( - logger.Named("svc"), &coordPtr, - time.Hour, - func() *tailcfg.DERPMap { panic("not implemented") }, - ) + svc, err := tailnet.NewClientService(tailnet.ClientServiceOptions{ + Logger: logger.Named("svc"), + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: time.Hour, + DERPMapFn: func() *tailcfg.DERPMap { panic("not implemented") }, + NetworkTelemetryHandler: func(batch []*proto.TelemetryEvent) { panic("not implemented") }, + }) require.NoError(t, err) sC, cC := net.Pipe() diff --git a/tailnet/service.go b/tailnet/service.go index a7b9de911456b..231f191f5ed82 100644 --- a/tailnet/service.go +++ b/tailnet/service.go @@ -36,6 +36,14 @@ func WithStreamID(ctx context.Context, streamID StreamID) context.Context { return context.WithValue(ctx, streamIDContextKey{}, streamID) } +type ClientServiceOptions struct { + Logger slog.Logger + CoordPtr *atomic.Pointer[Coordinator] + DERPMapUpdateFrequency time.Duration + DERPMapFn func() *tailcfg.DERPMap + NetworkTelemetryHandler func(batch []*proto.TelemetryEvent) +} + // ClientService is a tailnet coordination service that accepts a connection and version from a // tailnet client, and support versions 1.0 and 2.x of the Tailnet API protocol. type ClientService struct { @@ -46,21 +54,17 @@ type ClientService struct { // NewClientService returns a ClientService based on the given Coordinator pointer. The pointer is // loaded on each processed connection. -func NewClientService( - logger slog.Logger, - coordPtr *atomic.Pointer[Coordinator], - derpMapUpdateFrequency time.Duration, - derpMapFn func() *tailcfg.DERPMap, -) ( +func NewClientService(options ClientServiceOptions) ( *ClientService, error, ) { - s := &ClientService{Logger: logger, CoordPtr: coordPtr} + s := &ClientService{Logger: options.Logger, CoordPtr: options.CoordPtr} mux := drpcmux.New() drpcService := &DRPCService{ - CoordPtr: coordPtr, - Logger: logger, - DerpMapUpdateFrequency: derpMapUpdateFrequency, - DerpMapFn: derpMapFn, + CoordPtr: options.CoordPtr, + Logger: options.Logger, + DerpMapUpdateFrequency: options.DERPMapUpdateFrequency, + DerpMapFn: options.DERPMapFn, + NetworkTelemetryHandler: options.NetworkTelemetryHandler, } err := proto.DRPCRegisterTailnet(mux, drpcService) if err != nil { @@ -73,7 +77,7 @@ func NewClientService( xerrors.Is(err, context.DeadlineExceeded) { return } - logger.Debug(context.Background(), "drpc server error", slog.Error(err)) + options.Logger.Debug(context.Background(), "drpc server error", slog.Error(err)) }, }) s.drpc = server @@ -117,99 +121,17 @@ func (s ClientService) ServeConnV2(ctx context.Context, conn net.Conn, streamID // DRPCService is the dRPC-based, version 2.x of the tailnet API and implements proto.DRPCClientServer type DRPCService struct { - CoordPtr *atomic.Pointer[Coordinator] - Logger slog.Logger - DerpMapUpdateFrequency time.Duration - DerpMapFn func() *tailcfg.DERPMap - NetworkTelemetryBatchFrequency time.Duration - NetworkTelemetryBatchMaxSize int - NetworkTelemetryBatchFn func(batch []*proto.TelemetryEvent) - - mu sync.Mutex - networkEventBatchTicker *time.Ticker - pendingNetworkEvents []*proto.TelemetryEvent -} - -func (s *DRPCService) writeTelemetryEvents(events []*proto.TelemetryEvent) { - if s.NetworkTelemetryBatchFn == nil { - return - } - s.NetworkTelemetryBatchFn(events) -} - -func (s *DRPCService) sendTelemetryBatch() { - s.mu.Lock() - defer s.mu.Unlock() - events := s.pendingNetworkEvents - s.pendingNetworkEvents = []*proto.TelemetryEvent{} - go s.writeTelemetryEvents(events) -} - -// PeriodicTelemetryBatcher starts a goroutine to periodically send telemetry -// events to the telemetry backend. The returned function is a cleanup function -// that should be called when the service is no longer needed. Calling this more -// than once will panic. -// -// Note: calling the returned function does not unblock any in-flight calls to -// the underlying telemetry backend that come from PostTelemetry due to -// s.TelemetryBatchMaxSize. -func (s *DRPCService) PeriodicTelemetryBatcher() func() { - var ( - closed = make(chan struct{}) - done = make(chan struct{}) - ) - if s.NetworkTelemetryBatchFn == nil { - return func() {} - } - - s.mu.Lock() - defer s.mu.Unlock() - if s.networkEventBatchTicker != nil { - panic("PeriodicTelemetryBatcher called more than once") - } - ticker := time.NewTicker(s.NetworkTelemetryBatchFrequency) - s.networkEventBatchTicker = ticker - - go func() { - defer close(done) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - s.sendTelemetryBatch() - case <-closed: - // Send any remaining telemetry events before exiting. - s.sendTelemetryBatch() - return - } - } - }() - - return func() { - close(closed) - <-done - } + CoordPtr *atomic.Pointer[Coordinator] + Logger slog.Logger + DerpMapUpdateFrequency time.Duration + DerpMapFn func() *tailcfg.DERPMap + NetworkTelemetryHandler func(batch []*proto.TelemetryEvent) } func (s *DRPCService) PostTelemetry(_ context.Context, req *proto.TelemetryRequest) (*proto.TelemetryResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() - - for _, event := range req.Events { - s.pendingNetworkEvents = append(s.pendingNetworkEvents, event) - - if len(s.pendingNetworkEvents) >= s.NetworkTelemetryBatchMaxSize { - events := s.pendingNetworkEvents - s.pendingNetworkEvents = []*proto.TelemetryEvent{} - // Perform the send in a goroutine to avoid blocking the DRPC call. - if s.networkEventBatchTicker != nil { - s.networkEventBatchTicker.Reset(s.NetworkTelemetryBatchFrequency) - } - go s.writeTelemetryEvents(events) - } + if s.NetworkTelemetryHandler != nil { + s.NetworkTelemetryHandler(req.Events) } - return &proto.TelemetryResponse{}, nil } @@ -315,3 +237,83 @@ func (c communicator) loopResp() { } } } + +type NetworkTelemetryBatcher struct { + frequency time.Duration + maxSize int + batchFn func(batch []*proto.TelemetryEvent) + + mu sync.Mutex + closed chan struct{} + done chan struct{} + ticker *time.Ticker + pending []*proto.TelemetryEvent +} + +func NewNetworkTelemetryBatcher(frequency time.Duration, maxSize int, batchFn func(batch []*proto.TelemetryEvent)) *NetworkTelemetryBatcher { + b := &NetworkTelemetryBatcher{ + frequency: frequency, + maxSize: maxSize, + batchFn: batchFn, + closed: make(chan struct{}), + done: make(chan struct{}), + } + b.start() + return b +} + +func (b *NetworkTelemetryBatcher) Close() error { + close(b.closed) + <-b.done + return nil +} + +func (b *NetworkTelemetryBatcher) sendTelemetryBatch() { + b.mu.Lock() + defer b.mu.Unlock() + events := b.pending + b.pending = []*proto.TelemetryEvent{} + go b.batchFn(events) +} + +func (b *NetworkTelemetryBatcher) start() { + b.mu.Lock() + defer b.mu.Unlock() + ticker := time.NewTicker(b.frequency) + b.ticker = ticker + + go func() { + defer close(b.done) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + b.sendTelemetryBatch() + case <-b.closed: + // Send any remaining telemetry events before exiting. + b.sendTelemetryBatch() + return + } + } + }() +} + +func (b *NetworkTelemetryBatcher) Handler(events []*proto.TelemetryEvent) { + b.mu.Lock() + defer b.mu.Unlock() + + for _, event := range events { + b.pending = append(b.pending, event) + + if len(b.pending) >= b.maxSize { + events := b.pending + b.pending = []*proto.TelemetryEvent{} + // Perform the send in a goroutine to avoid blocking the DRPC call. + if b.ticker != nil { + b.ticker.Reset(b.frequency) + } + go b.batchFn(events) + } + } +} diff --git a/tailnet/service_test.go b/tailnet/service_test.go index 572d5ad2d7030..ddc4a52b002f9 100644 --- a/tailnet/service_test.go +++ b/tailnet/service_test.go @@ -28,10 +28,13 @@ func TestClientService_ServeClient_V2(t *testing.T) { coordPtr.Store(&coord) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) derpMap := &tailcfg.DERPMap{Regions: map[int]*tailcfg.DERPRegion{999: {RegionCode: "test"}}} - uut, err := tailnet.NewClientService( - logger, &coordPtr, - time.Millisecond, func() *tailcfg.DERPMap { return derpMap }, - ) + uut, err := tailnet.NewClientService(tailnet.ClientServiceOptions{ + Logger: logger, + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: time.Millisecond, + DERPMapFn: func() *tailcfg.DERPMap { return derpMap }, + NetworkTelemetryHandler: func(batch []*proto.TelemetryEvent) {}, + }) require.NoError(t, err) ctx := testutil.Context(t, testutil.WaitShort) @@ -96,6 +99,9 @@ func TestClientService_ServeClient_V2(t *testing.T) { err = dms.Close() require.NoError(t, err) + // PostTelemetry + // TODO: write test + // RPCs closed; we need to close the Conn to end the session. err = c.Close() require.NoError(t, err) @@ -110,7 +116,13 @@ func TestClientService_ServeClient_V1(t *testing.T) { coordPtr := atomic.Pointer[tailnet.Coordinator]{} coordPtr.Store(&coord) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) - uut, err := tailnet.NewClientService(logger, &coordPtr, 0, nil) + uut, err := tailnet.NewClientService(tailnet.ClientServiceOptions{ + Logger: logger, + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: 0, + DERPMapFn: nil, + NetworkTelemetryHandler: nil, + }) require.NoError(t, err) ctx := testutil.Context(t, testutil.WaitShort) diff --git a/tailnet/test/integration/integration.go b/tailnet/test/integration/integration.go index 938ed29e8d555..2f19ec43dec02 100644 --- a/tailnet/test/integration/integration.go +++ b/tailnet/test/integration/integration.go @@ -38,6 +38,7 @@ import ( "github.com/coder/coder/v2/coderd/tracing" "github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/tailnet" + tailnetproto "github.com/coder/coder/v2/tailnet/proto" "github.com/coder/coder/v2/testutil" ) @@ -169,11 +170,17 @@ func (o SimpleServerOptions) Router(t *testing.T, logger slog.Logger) *chi.Mux { conns: make(map[uuid.UUID]net.Conn), } - csvc, err := tailnet.NewClientService(logger, &coordPtr, 10*time.Minute, func() *tailcfg.DERPMap { - return &tailcfg.DERPMap{ - // Clients will set their own based on their custom access URL. - Regions: map[int]*tailcfg.DERPRegion{}, - } + csvc, err := tailnet.NewClientService(tailnet.ClientServiceOptions{ + Logger: logger, + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: 10 * time.Minute, + DERPMapFn: func() *tailcfg.DERPMap { + return &tailcfg.DERPMap{ + // Clients will set their own based on their custom access URL. + Regions: map[int]*tailcfg.DERPRegion{}, + } + }, + NetworkTelemetryHandler: func(batch []*tailnetproto.TelemetryEvent) {}, }) require.NoError(t, err) From 479df1fd1255ffb63a1ed70c4e50e5dc119e76ee Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Fri, 28 Jun 2024 07:17:21 +0000 Subject: [PATCH 5/6] Tests --- coderd/coderd.go | 2 + tailnet/service.go | 9 +++-- tailnet/service_test.go | 82 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index 02761d13a3646..7ff22634b1aa0 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -39,6 +39,7 @@ import ( "cdr.dev/slog" agentproto "github.com/coder/coder/v2/agent/proto" "github.com/coder/coder/v2/buildinfo" + "github.com/coder/coder/v2/clock" _ "github.com/coder/coder/v2/coderd/apidoc" // Used for swagger docs. "github.com/coder/coder/v2/coderd/appearance" "github.com/coder/coder/v2/coderd/audit" @@ -548,6 +549,7 @@ func New(options *Options) *API { options.PrometheusRegistry.MustRegister(stn) } api.NetworkTelemetryBatcher = tailnet.NewNetworkTelemetryBatcher( + clock.NewReal(), api.Options.NetworkTelemetryBatchFrequency, api.Options.NetworkTelemetryBatchMaxSize, api.handleNetworkTelemetry, diff --git a/tailnet/service.go b/tailnet/service.go index 231f191f5ed82..9ac086e850994 100644 --- a/tailnet/service.go +++ b/tailnet/service.go @@ -17,6 +17,7 @@ import ( "cdr.dev/slog" "github.com/coder/coder/v2/apiversion" + "github.com/coder/coder/v2/clock" "github.com/coder/coder/v2/tailnet/proto" ) @@ -239,6 +240,7 @@ func (c communicator) loopResp() { } type NetworkTelemetryBatcher struct { + clock clock.Clock frequency time.Duration maxSize int batchFn func(batch []*proto.TelemetryEvent) @@ -246,12 +248,13 @@ type NetworkTelemetryBatcher struct { mu sync.Mutex closed chan struct{} done chan struct{} - ticker *time.Ticker + ticker *clock.Ticker pending []*proto.TelemetryEvent } -func NewNetworkTelemetryBatcher(frequency time.Duration, maxSize int, batchFn func(batch []*proto.TelemetryEvent)) *NetworkTelemetryBatcher { +func NewNetworkTelemetryBatcher(clk clock.Clock, frequency time.Duration, maxSize int, batchFn func(batch []*proto.TelemetryEvent)) *NetworkTelemetryBatcher { b := &NetworkTelemetryBatcher{ + clock: clk, frequency: frequency, maxSize: maxSize, batchFn: batchFn, @@ -279,7 +282,7 @@ func (b *NetworkTelemetryBatcher) sendTelemetryBatch() { func (b *NetworkTelemetryBatcher) start() { b.mu.Lock() defer b.mu.Unlock() - ticker := time.NewTicker(b.frequency) + ticker := b.clock.NewTicker(b.frequency) b.ticker = ticker go func() { diff --git a/tailnet/service_test.go b/tailnet/service_test.go index ddc4a52b002f9..0bbe9c20e6662 100644 --- a/tailnet/service_test.go +++ b/tailnet/service_test.go @@ -8,12 +8,14 @@ import ( "time" "github.com/google/uuid" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/xerrors" "tailscale.com/tailcfg" "cdr.dev/slog" "cdr.dev/slog/sloggers/slogtest" + "github.com/coder/coder/v2/clock" "github.com/coder/coder/v2/tailnet" "github.com/coder/coder/v2/tailnet/proto" "github.com/coder/coder/v2/tailnet/tailnettest" @@ -28,12 +30,16 @@ func TestClientService_ServeClient_V2(t *testing.T) { coordPtr.Store(&coord) logger := slogtest.Make(t, nil).Leveled(slog.LevelDebug) derpMap := &tailcfg.DERPMap{Regions: map[int]*tailcfg.DERPRegion{999: {RegionCode: "test"}}} + + telemetryEvents := make(chan []*proto.TelemetryEvent, 64) uut, err := tailnet.NewClientService(tailnet.ClientServiceOptions{ - Logger: logger, - CoordPtr: &coordPtr, - DERPMapUpdateFrequency: time.Millisecond, - DERPMapFn: func() *tailcfg.DERPMap { return derpMap }, - NetworkTelemetryHandler: func(batch []*proto.TelemetryEvent) {}, + Logger: logger, + CoordPtr: &coordPtr, + DERPMapUpdateFrequency: time.Millisecond, + DERPMapFn: func() *tailcfg.DERPMap { return derpMap }, + NetworkTelemetryHandler: func(batch []*proto.TelemetryEvent) { + telemetryEvents <- batch + }, }) require.NoError(t, err) @@ -100,7 +106,23 @@ func TestClientService_ServeClient_V2(t *testing.T) { require.NoError(t, err) // PostTelemetry - // TODO: write test + telemetryReq := &proto.TelemetryRequest{ + Events: []*proto.TelemetryEvent{ + { + Id: []byte("hi"), + }, + { + Id: []byte("bye"), + }, + }, + } + res, err := client.PostTelemetry(ctx, telemetryReq) + require.NoError(t, err) + require.NotNil(t, res) + gotEvents := testutil.RequireRecvCtx(ctx, t, telemetryEvents) + require.Len(t, gotEvents, 2) + require.Equal(t, "hi", string(gotEvents[0].Id)) + require.Equal(t, "bye", string(gotEvents[1].Id)) // RPCs closed; we need to close the Conn to end the session. err = c.Close() @@ -154,3 +176,51 @@ func TestClientService_ServeClient_V1(t *testing.T) { err = testutil.RequireRecvCtx(ctx, t, errCh) require.ErrorIs(t, err, expectedError) } + +func TestNetworkTelemetryBatcher(t *testing.T) { + t.Parallel() + + var ( + events = make(chan []*proto.TelemetryEvent, 64) + mClock = clock.NewMock(t) + b = tailnet.NewNetworkTelemetryBatcher(mClock, time.Millisecond, 3, func(batch []*proto.TelemetryEvent) { + assert.LessOrEqual(t, len(batch), 3) + events <- batch + }) + ) + + b.Handler([]*proto.TelemetryEvent{ + {Id: []byte("1")}, + {Id: []byte("2")}, + }) + b.Handler([]*proto.TelemetryEvent{ + {Id: []byte("3")}, + {Id: []byte("4")}, + }) + + // Should overflow and send a batch. + ctx := testutil.Context(t, testutil.WaitShort) + batch := testutil.RequireRecvCtx(ctx, t, events) + require.Len(t, batch, 3) + require.Equal(t, "1", string(batch[0].Id)) + require.Equal(t, "2", string(batch[1].Id)) + require.Equal(t, "3", string(batch[2].Id)) + + // Should send any pending events when the ticker fires. + mClock.Advance(time.Millisecond) + batch = testutil.RequireRecvCtx(ctx, t, events) + require.Len(t, batch, 1) + require.Equal(t, "4", string(batch[0].Id)) + + // Should send any pending events when closed. + b.Handler([]*proto.TelemetryEvent{ + {Id: []byte("5")}, + {Id: []byte("6")}, + }) + err := b.Close() + require.NoError(t, err) + batch = testutil.RequireRecvCtx(ctx, t, events) + require.Len(t, batch, 2) + require.Equal(t, "5", string(batch[0].Id)) + require.Equal(t, "6", string(batch[1].Id)) +} From 6a40a02280e8bda89b8d4ec06f6cf5e36422ef1d Mon Sep 17 00:00:00 2001 From: Dean Sheather Date: Mon, 1 Jul 2024 11:04:36 +0000 Subject: [PATCH 6/6] Comments --- coderd/coderd.go | 7 +++++- coderd/telemetry/telemetry.go | 30 ++++++++++++++--------- tailnet/service.go | 45 +++++++++++++++++++++++++---------- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/coderd/coderd.go b/coderd/coderd.go index 7ff22634b1aa0..f399801f67dd8 100644 --- a/coderd/coderd.go +++ b/coderd/coderd.go @@ -1330,7 +1330,12 @@ type API struct { // Close waits for all WebSocket connections to drain before returning. func (api *API) Close() error { - api.cancel() + select { + case <-api.ctx.Done(): + return xerrors.New("API already closed") + default: + api.cancel() + } if api.derpCloseFunc != nil { api.derpCloseFunc() } diff --git a/coderd/telemetry/telemetry.go b/coderd/telemetry/telemetry.go index e35475c4add71..3692d6eb5cbee 100644 --- a/coderd/telemetry/telemetry.go +++ b/coderd/telemetry/telemetry.go @@ -1249,12 +1249,12 @@ type NetworkEvent struct { DERPMap DERPMap `json:"derp_map"` LatestNetcheck Netcheck `json:"latest_netcheck"` - ConnectionAge time.Duration `json:"connection_age"` - ConnectionSetup time.Duration `json:"connection_setup"` - P2PSetup time.Duration `json:"p2p_setup"` - DERPLatency time.Duration `json:"derp_latency"` - P2PLatency time.Duration `json:"p2p_latency"` - ThroughputMbits *float32 `json:"throughput_mbits"` + ConnectionAge *time.Duration `json:"connection_age"` + ConnectionSetup *time.Duration `json:"connection_setup"` + P2PSetup *time.Duration `json:"p2p_setup"` + DERPLatency *time.Duration `json:"derp_latency"` + P2PLatency *time.Duration `json:"p2p_latency"` + ThroughputMbits *float32 `json:"throughput_mbits"` } func protoFloat(f *wrapperspb.FloatValue) *float32 { @@ -1264,6 +1264,14 @@ func protoFloat(f *wrapperspb.FloatValue) *float32 { return &f.Value } +func protoDurationNil(d *durationpb.Duration) *time.Duration { + if d == nil { + return nil + } + dur := d.AsDuration() + return &dur +} + func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, error) { if proto == nil { return NetworkEvent{}, xerrors.New("nil event") @@ -1294,11 +1302,11 @@ func NetworkEventFromProto(proto *tailnetproto.TelemetryEvent) (NetworkEvent, er DERPMap: derpMapFromProto(proto.DerpMap), LatestNetcheck: netcheckFromProto(proto.LatestNetcheck), - ConnectionAge: proto.ConnectionAge.AsDuration(), - ConnectionSetup: proto.ConnectionSetup.AsDuration(), - P2PSetup: proto.P2PSetup.AsDuration(), - DERPLatency: proto.DerpLatency.AsDuration(), - P2PLatency: proto.P2PLatency.AsDuration(), + ConnectionAge: protoDurationNil(proto.ConnectionAge), + ConnectionSetup: protoDurationNil(proto.ConnectionSetup), + P2PSetup: protoDurationNil(proto.P2PSetup), + DERPLatency: protoDurationNil(proto.DerpLatency), + P2PLatency: protoDurationNil(proto.P2PLatency), ThroughputMbits: protoFloat(proto.ThroughputMbits), }, nil } diff --git a/tailnet/service.go b/tailnet/service.go index 9ac086e850994..d5842151ecb26 100644 --- a/tailnet/service.go +++ b/tailnet/service.go @@ -267,7 +267,13 @@ func NewNetworkTelemetryBatcher(clk clock.Clock, frequency time.Duration, maxSiz func (b *NetworkTelemetryBatcher) Close() error { close(b.closed) - <-b.done + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + select { + case <-ctx.Done(): + return xerrors.New("timed out waiting for batcher to close") + case <-b.done: + } return nil } @@ -275,24 +281,30 @@ func (b *NetworkTelemetryBatcher) sendTelemetryBatch() { b.mu.Lock() defer b.mu.Unlock() events := b.pending + if len(events) == 0 { + return + } b.pending = []*proto.TelemetryEvent{} - go b.batchFn(events) + b.batchFn(events) } func (b *NetworkTelemetryBatcher) start() { - b.mu.Lock() - defer b.mu.Unlock() - ticker := b.clock.NewTicker(b.frequency) - b.ticker = ticker + b.ticker = b.clock.NewTicker(b.frequency) go func() { - defer close(b.done) - defer ticker.Stop() + defer func() { + // The lock prevents Handler from racing with Close. + b.mu.Lock() + defer b.mu.Unlock() + close(b.done) + b.ticker.Stop() + }() for { select { - case <-ticker.C: + case <-b.ticker.C: b.sendTelemetryBatch() + b.ticker.Reset(b.frequency) case <-b.closed: // Send any remaining telemetry events before exiting. b.sendTelemetryBatch() @@ -305,17 +317,26 @@ func (b *NetworkTelemetryBatcher) start() { func (b *NetworkTelemetryBatcher) Handler(events []*proto.TelemetryEvent) { b.mu.Lock() defer b.mu.Unlock() + select { + case <-b.closed: + return + default: + } for _, event := range events { b.pending = append(b.pending, event) if len(b.pending) >= b.maxSize { + // This can't call sendTelemetryBatch directly because we already + // hold the lock. events := b.pending b.pending = []*proto.TelemetryEvent{} + // Resetting the ticker is best effort. We don't care if the ticker + // has already fired or has a pending message, because the only risk + // is that we send two telemetry events in short succession (which + // is totally fine). + b.ticker.Reset(b.frequency) // Perform the send in a goroutine to avoid blocking the DRPC call. - if b.ticker != nil { - b.ticker.Reset(b.frequency) - } go b.batchFn(events) } }