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) } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy