diff --git a/CHANGELOG.md b/CHANGELOG.md index 59c68e704..f7dc9446e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,235 @@ -# Release 1.1.16 +# Release 1.2.0 ## What's New -* [Issue #2468](https://github.com/openziti/ziti/issues/2468) - Controller configuration `edge.api.address` selects the - enrollment token signer by matching the DNS SAN of a server certificate. If more than one server certificate - matches then the wrong key may sign the tokens causing enrollments to fail with a signature verification error. +* New Router Metrics +* Changes to identity connect status +* HA Bootstrap Changes +* Connect Events +* SDK Events +* Bug fixes and other HA work + +## New Router Metrics + +The following new metrics are available for edge routers: + +1. edge.connect.failures - meter tracking failed connect attempts from sdks + This tracks failures to not having a valid token. Other failures which + happen earlier in the connection process may not be tracked here. +2. edge.connect.successes - meter tracking successful connect attempts from sdks +3. edge.disconnects - meter tracking disconnects of previously successfully connected sdks +4. edge.connections - gauge tracking count of currently connected sdks + +## Identity Connect Status + +Ziti tracks whether an identity is currently connected to an edge router. +This is the `hasEdgeRouterConnection` field on Identity. + +Identity connection status used to be driven off of heartbeats from the edge router. +This feature doesn't work correctly when running with controller HA. + +To address this, while also providing more operation insight, connect events were added +(see below for more details on the events themselves). + +The controller can be configured to use status from heartbeats, connect events or both. +If both are used as source, then if either reports the identity as connected, then it +will show as connected. This is intended for when you have a mix of routers and they +don't all yet supported connect events. + +The controller now also aims to be more precise about identity state. There is a new +field on Identity: `edgeRouterConnectionStatus`. This field can have one of three +values: + +* offline +* online +* unknown + +If the identity is reported as connected to any ER, it will be marked as `online`. +If the identity has been reported as connected, but the reporting ER is now +offline, the identity may still be connected to the ER. While in this state +it will be marked as 'unknown'. After a configurable interval, it will be marked +as offline. + +New controller config options: + +``` +identityStatusConfig: + # valid values ['heartbeats', 'connect-events', 'hybrid'] + # defaults to 'hybrid' for now + source: connect-events + + # determines how often we scan for disconnected routers + # defaults to 1 minute + scanInterval: 1m + + # determines how long an identity will stay in unknown status before it's marked as offline + # defaults to 5m + unknownTimeout: 5m +``` + +## HA Bootstrapping Changes + +Previously bootstrapping the RAFT cluster and initializing the controller with a +default administrator were separate operations. +Now, the raft cluster will be bootstrapped whenever the controller is initialized. + +The controller can be initialized as follows: + +1. Using `ziti agent controller init` +2. Using `ziti agent controller init-from-db` +3. Specifying a `db:` entry in the config file. This is equivalent to using `ziti agent controller init-from-db`. + +Additionally: + +1. `minClusterSize` has been removed. The cluster will always be initialized with a size of 1. +2. `bootstrapMembers` has been renamed to `initialMembers`. If `initialMembers` are specified, + the bootstrapping controller will attempt to add them after bootstrap has been complete. If + they are invalid they will be ignored. If they can't be reached (because they're not running + yet), the controller will continue to retry until they are reached, or it is restarted. + + +## Connect Events + +These are events generated when a successful connection is made to a controller, from any of: + +1. Identity, using the REST API +2. Router +3. Controller (peer in an HA cluster) + +They are also generated when an SDK connects to a router. + +**Controller Configuration** + +```yml +events: + jsonLogger: + subscriptions: + - type: connect + handler: + type: file + format: json + path: /tmp/ziti-events.log +``` + +**Router Configuration** +```yml +connectEvents: + # defaults to true. + # If set to false, minimal information about which identities are connected will still be + # sent to the controller, so the `edgeRouterConnectionStatus` field can be populated, + # but connect events will not be generated. + enabled: true + + # The interval at which connect information will be batched up and sent to the controller. + # Shorter intervals will improve data resolution on the controller. Longer intervals could + # more efficient. + batchInterval: 3s + + # The router will also periodically sent the full state to the controller, to ensure that + # it's in sync. It will do this automatically if the router gets disconnected from the + # controller, or if the router is unable to send a connect events messages to the controller. + # This controls how often the full state will be sent under ordinairy conditions + fullSyncInterval: 5m + + # If enabled is set to true, the router will collect connect events and send them out + # at the configured batch interval. If there are a huge number of connecting identities + # or if the router is disconnected from the controller for a time, it may be unable to + # send events. In order to prevent queued events from exhausting memory, a maximum + # queue size is configured. + # Default value 100,000 + maxQueuedEvents: 100000 + +``` + +**Example Events** + +```json +{ + "namespace": "connect", + "src_type": "identity", + "src_id": "ji2Rt8KJ4", + "src_addr": "127.0.0.1:59336", + "dst_id": "ctrl_client", + "dst_addr": "localhost:1280/edge/management/v1/edge-routers/2L7NeVuGBU", + "timestamp": "2024-10-02T12:17:39.501821249-04:00" +} +{ + "namespace": "connect", + "src_type": "router", + "src_id": "2L7NeVuGBU", + "src_addr": "127.0.0.1:42702", + "dst_id": "ctrl_client", + "dst_addr": "127.0.0.1:6262", + "timestamp": "2024-10-02T12:17:40.529865849-04:00" +} +{ + "namespace": "connect", + "src_type": "peer", + "src_id": "ctrl2", + "src_addr": "127.0.0.1:40056", + "dst_id": "ctrl1", + "dst_addr": "127.0.0.1:6262", + "timestamp": "2024-10-02T12:37:04.490859197-04:00" +} +``` + +## SDK Events + +Building off of the connect events, there are events generated when an identity/sdk comes online or goes offline. + +```yml +events: + jsonLogger: + subscriptions: + - type: sdk + handler: + type: file + format: json + path: /tmp/ziti-events.log +``` + +```json +{ + "namespace": "sdk", + "event_type" : "sdk-online", + "identity_id": "ji2Rt8KJ4", + "timestamp": "2024-10-02T12:17:39.501821249-04:00" +} + +{ + "namespace": "sdk", + "event_type" : "sdk-status-unknown", + "identity_id": "ji2Rt8KJ4", + "timestamp": "2024-10-02T12:17:40.501821249-04:00" +} + +{ + "namespace": "sdk", + "event_type" : "sdk-offline", + "identity_id": "ji2Rt8KJ4", + "timestamp": "2024-10-02T12:17:41.501821249-04:00" +} +``` + +## Component Updates and Bug Fixes + +* github.com/openziti/channel/v3: [v3.0.5 -> v3.0.7](https://github.com/openziti/channel/compare/v3.0.5...v3.0.7) +* github.com/openziti/edge-api: [v0.26.32 -> v0.26.35](https://github.com/openziti/edge-api/compare/v0.26.32...v0.26.35) +* github.com/openziti/identity: [v1.0.85 -> v1.0.87](https://github.com/openziti/identity/compare/v1.0.85...v1.0.87) + +* github.com/openziti/sdk-golang: [v0.23.43 -> v0.23.44](https://github.com/openziti/sdk-golang/compare/v0.23.43...v0.23.44) +* github.com/openziti/transport/v2: [v2.0.146 -> v2.0.148](https://github.com/openziti/transport/compare/v2.0.146...v2.0.148) +* github.com/openziti/ziti: [v1.1.15 -> v1.2.0](https://github.com/openziti/ziti/compare/v1.1.15...v1.2.0) + * [Issue #2212](https://github.com/openziti/ziti/issues/2212) - Rework distributed control bootstrap mechanism + * [Issue #1835](https://github.com/openziti/ziti/issues/1835) - Add access log for rest and router connections + * [Issue #2234](https://github.com/openziti/ziti/issues/2234) - Emit an event when hasEdgeRouterConnection state changes for an Identity + * [Issue #2491](https://github.com/openziti/ziti/issues/2491) - fix router CSR loading + * [Issue #2478](https://github.com/openziti/ziti/issues/2478) - JWT signer secondary auth: not enough information to continue + * [Issue #2482](https://github.com/openziti/ziti/issues/2482) - router run command - improperly binds 127.0.0.1:53/udp when tunnel mode is not tproxy + * [Issue #2474](https://github.com/openziti/ziti/issues/2474) - Enable Ext JWT Enrollment/Generic Trust Bootstrapping + * [Issue #2471](https://github.com/openziti/ziti/issues/2471) - Service Access for Legacy SDKs in HA does not work + * [Issue #2468](https://github.com/openziti/ziti/issues/2468) - enrollment signing cert is not properly identified + # Release 1.1.15 diff --git a/common/inspect/connect_inspections.go b/common/inspect/connect_inspections.go new file mode 100644 index 000000000..3598c22cb --- /dev/null +++ b/common/inspect/connect_inspections.go @@ -0,0 +1,62 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package inspect + +const ( + RouterIdentityConnectionStatusesKey = "identity-connection-statuses" +) + +type RouterIdentityConnections struct { + IdentityConnections map[string]*RouterIdentityConnectionDetail `json:"identity_connections"` + LastFullSync string `json:"last_full_sync"` + QueuedEventCount int64 `json:"queued_event_count"` + MaxQueuedEvents int64 `json:"max_queued_events"` + NeedFullSync []string `json:"need_full_sync"` + BatchInterval string `json:"batch_interval"` + FullSyncInterval string `json:"full_sync_interval"` +} + +type RouterIdentityConnectionDetail struct { + UnreportedCount uint64 `json:"unreported_count"` + UnreportedStateChanged bool `json:"unreported_state_changed"` + BeingReportedCount uint64 `json:"being_reported_count"` + BeingReportedStateChanged bool `json:"being_reported_state_changed"` + Connections []*RouterConnectionDetail `json:"connections"` +} + +type RouterConnectionDetail struct { + Id string `json:"id"` + Closed bool `json:"closed"` + SrcAddr string `json:"srcAddr"` + DstAddr string `json:"dstAddr"` +} + +type CtrlIdentityConnections struct { + Connections map[string]*CtrlIdentityConnectionDetail `json:"connections"` + ScanInterval string `json:"scanInterval"` +} + +type CtrlIdentityConnectionDetail struct { + ConnectedRouters map[string]*CtrlRouterConnection `json:"connected_routers"` + LastReportedState string `json:"last_reported_state"` +} + +type CtrlRouterConnection struct { + RouterId string `json:"router_id"` + Closed bool `json:"closed"` + TimeSinceLastWrite string `json:"time_since_last_write"` +} diff --git a/common/oidc_tokens.go b/common/oidc_tokens.go index 5c72a2bbe..3461b1269 100644 --- a/common/oidc_tokens.go +++ b/common/oidc_tokens.go @@ -110,7 +110,7 @@ func (r *RefreshClaims) GetIssuer() (string, error) { } func (r *RefreshClaims) GetSubject() (string, error) { - return r.TokenClaims.Issuer, nil + return r.TokenClaims.Subject, nil } func (r *RefreshClaims) GetAudience() (jwt.ClaimStrings, error) { @@ -211,7 +211,7 @@ func (r *AccessClaims) GetIssuer() (string, error) { } func (r *AccessClaims) GetSubject() (string, error) { - return r.TokenClaims.Issuer, nil + return r.TokenClaims.Subject, nil } func (r *AccessClaims) GetAudience() (jwt.ClaimStrings, error) { @@ -260,7 +260,7 @@ func (r *IdTokenClaims) GetIssuer() (string, error) { } func (r *IdTokenClaims) GetSubject() (string, error) { - return r.TokenClaims.Issuer, nil + return r.TokenClaims.Subject, nil } func (r *IdTokenClaims) GetAudience() (jwt.ClaimStrings, error) { diff --git a/common/pb/edge_cmd_pb/edge_cmd.pb.go b/common/pb/edge_cmd_pb/edge_cmd.pb.go index c7547a737..e160db530 100644 --- a/common/pb/edge_cmd_pb/edge_cmd.pb.go +++ b/common/pb/edge_cmd_pb/edge_cmd.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.23.4 +// protoc v3.21.12 // source: edge_cmd.proto package edge_cmd_pb diff --git a/common/pb/edge_ctrl_pb/edge_ctrl.pb.go b/common/pb/edge_ctrl_pb/edge_ctrl.pb.go index ec6869e3f..b97284919 100644 --- a/common/pb/edge_ctrl_pb/edge_ctrl.pb.go +++ b/common/pb/edge_ctrl_pb/edge_ctrl.pb.go @@ -71,6 +71,7 @@ const ( ContentType_TunnelHealthEventType ContentType = 20412 ContentType_CreateTunnelTerminatorRequestV2Type ContentType = 20413 ContentType_CreateTunnelTerminatorResponseV2Type ContentType = 20414 + ContentType_ConnectEventsTypes ContentType = 20415 ContentType_DataStateType ContentType = 20500 ContentType_DataStateChangeSetType ContentType = 20501 ContentType_UpdateTokenType ContentType = 20502 @@ -124,6 +125,7 @@ var ( 20412: "TunnelHealthEventType", 20413: "CreateTunnelTerminatorRequestV2Type", 20414: "CreateTunnelTerminatorResponseV2Type", + 20415: "ConnectEventsTypes", 20500: "DataStateType", 20501: "DataStateChangeSetType", 20502: "UpdateTokenType", @@ -174,6 +176,7 @@ var ( "TunnelHealthEventType": 20412, "CreateTunnelTerminatorRequestV2Type": 20413, "CreateTunnelTerminatorResponseV2Type": 20414, + "ConnectEventsTypes": 20415, "DataStateType": 20500, "DataStateChangeSetType": 20501, "UpdateTokenType": 20502, @@ -1060,6 +1063,7 @@ type ApiSession struct { Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"` CertFingerprints []string `protobuf:"bytes,2,rep,name=certFingerprints,proto3" json:"certFingerprints,omitempty"` Id string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + IdentityId string `protobuf:"bytes,4,opt,name=identityId,proto3" json:"identityId,omitempty"` } func (x *ApiSession) Reset() { @@ -1115,6 +1119,13 @@ func (x *ApiSession) GetId() string { return "" } +func (x *ApiSession) GetIdentityId() string { + if x != nil { + return x.IdentityId + } + return "" +} + type ApiSessionAdded struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3585,6 +3596,61 @@ func (x *EnrollmentExtendRouterVerifyRequest) GetClientCertPem() string { return "" } +type ConnectEvents struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Events []*ConnectEvents_IdentityConnectEvents `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + FullState bool `protobuf:"varint,2,opt,name=fullState,proto3" json:"fullState,omitempty"` +} + +func (x *ConnectEvents) Reset() { + *x = ConnectEvents{} + if protoimpl.UnsafeEnabled { + mi := &file_edge_ctrl_proto_msgTypes[41] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectEvents) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectEvents) ProtoMessage() {} + +func (x *ConnectEvents) ProtoReflect() protoreflect.Message { + mi := &file_edge_ctrl_proto_msgTypes[41] + 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 ConnectEvents.ProtoReflect.Descriptor instead. +func (*ConnectEvents) Descriptor() ([]byte, []int) { + return file_edge_ctrl_proto_rawDescGZIP(), []int{41} +} + +func (x *ConnectEvents) GetEvents() []*ConnectEvents_IdentityConnectEvents { + if x != nil { + return x.Events + } + return nil +} + +func (x *ConnectEvents) GetFullState() bool { + if x != nil { + return x.FullState + } + return false +} + type DataState_ConfigType struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3597,7 +3663,7 @@ type DataState_ConfigType struct { func (x *DataState_ConfigType) Reset() { *x = DataState_ConfigType{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[44] + mi := &file_edge_ctrl_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3610,7 +3676,7 @@ func (x *DataState_ConfigType) String() string { func (*DataState_ConfigType) ProtoMessage() {} func (x *DataState_ConfigType) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[44] + mi := &file_edge_ctrl_proto_msgTypes[45] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3654,7 +3720,7 @@ type DataState_Config struct { func (x *DataState_Config) Reset() { *x = DataState_Config{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[45] + mi := &file_edge_ctrl_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3667,7 +3733,7 @@ func (x *DataState_Config) String() string { func (*DataState_Config) ProtoMessage() {} func (x *DataState_Config) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[45] + mi := &file_edge_ctrl_proto_msgTypes[46] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3722,7 +3788,7 @@ type DataState_ServiceConfigs struct { func (x *DataState_ServiceConfigs) Reset() { *x = DataState_ServiceConfigs{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[46] + mi := &file_edge_ctrl_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3735,7 +3801,7 @@ func (x *DataState_ServiceConfigs) String() string { func (*DataState_ServiceConfigs) ProtoMessage() {} func (x *DataState_ServiceConfigs) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[46] + mi := &file_edge_ctrl_proto_msgTypes[47] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3777,7 +3843,7 @@ type DataState_Identity struct { func (x *DataState_Identity) Reset() { *x = DataState_Identity{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[47] + mi := &file_edge_ctrl_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3790,7 +3856,7 @@ func (x *DataState_Identity) String() string { func (*DataState_Identity) ProtoMessage() {} func (x *DataState_Identity) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[47] + mi := &file_edge_ctrl_proto_msgTypes[48] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3883,7 +3949,7 @@ type DataState_Service struct { func (x *DataState_Service) Reset() { *x = DataState_Service{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[48] + mi := &file_edge_ctrl_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3896,7 +3962,7 @@ func (x *DataState_Service) String() string { func (*DataState_Service) ProtoMessage() {} func (x *DataState_Service) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[48] + mi := &file_edge_ctrl_proto_msgTypes[49] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3953,7 +4019,7 @@ type DataState_ServicePolicy struct { func (x *DataState_ServicePolicy) Reset() { *x = DataState_ServicePolicy{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[49] + mi := &file_edge_ctrl_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3966,7 +4032,7 @@ func (x *DataState_ServicePolicy) String() string { func (*DataState_ServicePolicy) ProtoMessage() {} func (x *DataState_ServicePolicy) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[49] + mi := &file_edge_ctrl_proto_msgTypes[50] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4015,7 +4081,7 @@ type DataState_Revocation struct { func (x *DataState_Revocation) Reset() { *x = DataState_Revocation{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[50] + mi := &file_edge_ctrl_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4028,7 +4094,7 @@ func (x *DataState_Revocation) String() string { func (*DataState_Revocation) ProtoMessage() {} func (x *DataState_Revocation) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[50] + mi := &file_edge_ctrl_proto_msgTypes[51] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4072,7 +4138,7 @@ type DataState_ServicePolicyChange struct { func (x *DataState_ServicePolicyChange) Reset() { *x = DataState_ServicePolicyChange{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[51] + mi := &file_edge_ctrl_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4085,7 +4151,7 @@ func (x *DataState_ServicePolicyChange) String() string { func (*DataState_ServicePolicyChange) ProtoMessage() {} func (x *DataState_ServicePolicyChange) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[51] + mi := &file_edge_ctrl_proto_msgTypes[52] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4142,7 +4208,7 @@ type DataState_ChangeSet struct { func (x *DataState_ChangeSet) Reset() { *x = DataState_ChangeSet{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[52] + mi := &file_edge_ctrl_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4155,7 +4221,7 @@ func (x *DataState_ChangeSet) String() string { func (*DataState_ChangeSet) ProtoMessage() {} func (x *DataState_ChangeSet) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[52] + mi := &file_edge_ctrl_proto_msgTypes[53] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4217,7 +4283,7 @@ type DataState_Event struct { func (x *DataState_Event) Reset() { *x = DataState_Event{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[53] + mi := &file_edge_ctrl_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4230,7 +4296,7 @@ func (x *DataState_Event) String() string { func (*DataState_Event) ProtoMessage() {} func (x *DataState_Event) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[53] + mi := &file_edge_ctrl_proto_msgTypes[54] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4402,7 +4468,7 @@ type DataState_PublicKey struct { func (x *DataState_PublicKey) Reset() { *x = DataState_PublicKey{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[54] + mi := &file_edge_ctrl_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4415,7 +4481,7 @@ func (x *DataState_PublicKey) String() string { func (*DataState_PublicKey) ProtoMessage() {} func (x *DataState_PublicKey) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[54] + mi := &file_edge_ctrl_proto_msgTypes[55] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4481,7 +4547,7 @@ type DataState_PostureCheck struct { func (x *DataState_PostureCheck) Reset() { *x = DataState_PostureCheck{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[55] + mi := &file_edge_ctrl_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4494,7 +4560,7 @@ func (x *DataState_PostureCheck) String() string { func (*DataState_PostureCheck) ProtoMessage() {} func (x *DataState_PostureCheck) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[55] + mi := &file_edge_ctrl_proto_msgTypes[56] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4631,7 +4697,7 @@ type DataState_PostureCheck_Mac struct { func (x *DataState_PostureCheck_Mac) Reset() { *x = DataState_PostureCheck_Mac{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[60] + mi := &file_edge_ctrl_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4644,7 +4710,7 @@ func (x *DataState_PostureCheck_Mac) String() string { func (*DataState_PostureCheck_Mac) ProtoMessage() {} func (x *DataState_PostureCheck_Mac) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[60] + mi := &file_edge_ctrl_proto_msgTypes[61] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4681,7 +4747,7 @@ type DataState_PostureCheck_Mfa struct { func (x *DataState_PostureCheck_Mfa) Reset() { *x = DataState_PostureCheck_Mfa{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[61] + mi := &file_edge_ctrl_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4694,7 +4760,7 @@ func (x *DataState_PostureCheck_Mfa) String() string { func (*DataState_PostureCheck_Mfa) ProtoMessage() {} func (x *DataState_PostureCheck_Mfa) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[61] + mi := &file_edge_ctrl_proto_msgTypes[62] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4750,7 +4816,7 @@ type DataState_PostureCheck_Os struct { func (x *DataState_PostureCheck_Os) Reset() { *x = DataState_PostureCheck_Os{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[62] + mi := &file_edge_ctrl_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4763,7 +4829,7 @@ func (x *DataState_PostureCheck_Os) String() string { func (*DataState_PostureCheck_Os) ProtoMessage() {} func (x *DataState_PostureCheck_Os) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[62] + mi := &file_edge_ctrl_proto_msgTypes[63] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4804,7 +4870,7 @@ type DataState_PostureCheck_OsList struct { func (x *DataState_PostureCheck_OsList) Reset() { *x = DataState_PostureCheck_OsList{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[63] + mi := &file_edge_ctrl_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4817,7 +4883,7 @@ func (x *DataState_PostureCheck_OsList) String() string { func (*DataState_PostureCheck_OsList) ProtoMessage() {} func (x *DataState_PostureCheck_OsList) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[63] + mi := &file_edge_ctrl_proto_msgTypes[64] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4854,7 +4920,7 @@ type DataState_PostureCheck_Process struct { func (x *DataState_PostureCheck_Process) Reset() { *x = DataState_PostureCheck_Process{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[64] + mi := &file_edge_ctrl_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4867,7 +4933,7 @@ func (x *DataState_PostureCheck_Process) String() string { func (*DataState_PostureCheck_Process) ProtoMessage() {} func (x *DataState_PostureCheck_Process) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[64] + mi := &file_edge_ctrl_proto_msgTypes[65] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4923,7 +4989,7 @@ type DataState_PostureCheck_ProcessMulti struct { func (x *DataState_PostureCheck_ProcessMulti) Reset() { *x = DataState_PostureCheck_ProcessMulti{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[65] + mi := &file_edge_ctrl_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4936,7 +5002,7 @@ func (x *DataState_PostureCheck_ProcessMulti) String() string { func (*DataState_PostureCheck_ProcessMulti) ProtoMessage() {} func (x *DataState_PostureCheck_ProcessMulti) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[65] + mi := &file_edge_ctrl_proto_msgTypes[66] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4977,7 +5043,7 @@ type DataState_PostureCheck_Domains struct { func (x *DataState_PostureCheck_Domains) Reset() { *x = DataState_PostureCheck_Domains{} if protoimpl.UnsafeEnabled { - mi := &file_edge_ctrl_proto_msgTypes[66] + mi := &file_edge_ctrl_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4990,7 +5056,7 @@ func (x *DataState_PostureCheck_Domains) String() string { func (*DataState_PostureCheck_Domains) ProtoMessage() {} func (x *DataState_PostureCheck_Domains) ProtoReflect() protoreflect.Message { - mi := &file_edge_ctrl_proto_msgTypes[66] + mi := &file_edge_ctrl_proto_msgTypes[67] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5013,6 +5079,132 @@ func (x *DataState_PostureCheck_Domains) GetDomains() []string { return nil } +type ConnectEvents_ConnectDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ConnectTime int64 `protobuf:"varint,1,opt,name=connectTime,proto3" json:"connectTime,omitempty"` + SrcAddr string `protobuf:"bytes,2,opt,name=srcAddr,proto3" json:"srcAddr,omitempty"` + DstAddr string `protobuf:"bytes,3,opt,name=dstAddr,proto3" json:"dstAddr,omitempty"` +} + +func (x *ConnectEvents_ConnectDetails) Reset() { + *x = ConnectEvents_ConnectDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_edge_ctrl_proto_msgTypes[83] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectEvents_ConnectDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectEvents_ConnectDetails) ProtoMessage() {} + +func (x *ConnectEvents_ConnectDetails) ProtoReflect() protoreflect.Message { + mi := &file_edge_ctrl_proto_msgTypes[83] + 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 ConnectEvents_ConnectDetails.ProtoReflect.Descriptor instead. +func (*ConnectEvents_ConnectDetails) Descriptor() ([]byte, []int) { + return file_edge_ctrl_proto_rawDescGZIP(), []int{41, 0} +} + +func (x *ConnectEvents_ConnectDetails) GetConnectTime() int64 { + if x != nil { + return x.ConnectTime + } + return 0 +} + +func (x *ConnectEvents_ConnectDetails) GetSrcAddr() string { + if x != nil { + return x.SrcAddr + } + return "" +} + +func (x *ConnectEvents_ConnectDetails) GetDstAddr() string { + if x != nil { + return x.DstAddr + } + return "" +} + +type ConnectEvents_IdentityConnectEvents struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + IdentityId string `protobuf:"bytes,1,opt,name=identityId,proto3" json:"identityId,omitempty"` + IsConnected bool `protobuf:"varint,2,opt,name=isConnected,proto3" json:"isConnected,omitempty"` + ConnectTimes []*ConnectEvents_ConnectDetails `protobuf:"bytes,3,rep,name=connectTimes,proto3" json:"connectTimes,omitempty"` +} + +func (x *ConnectEvents_IdentityConnectEvents) Reset() { + *x = ConnectEvents_IdentityConnectEvents{} + if protoimpl.UnsafeEnabled { + mi := &file_edge_ctrl_proto_msgTypes[84] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ConnectEvents_IdentityConnectEvents) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ConnectEvents_IdentityConnectEvents) ProtoMessage() {} + +func (x *ConnectEvents_IdentityConnectEvents) ProtoReflect() protoreflect.Message { + mi := &file_edge_ctrl_proto_msgTypes[84] + 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 ConnectEvents_IdentityConnectEvents.ProtoReflect.Descriptor instead. +func (*ConnectEvents_IdentityConnectEvents) Descriptor() ([]byte, []int) { + return file_edge_ctrl_proto_rawDescGZIP(), []int{41, 1} +} + +func (x *ConnectEvents_IdentityConnectEvents) GetIdentityId() string { + if x != nil { + return x.IdentityId + } + return "" +} + +func (x *ConnectEvents_IdentityConnectEvents) GetIsConnected() bool { + if x != nil { + return x.IsConnected + } + return false +} + +func (x *ConnectEvents_IdentityConnectEvents) GetConnectTimes() []*ConnectEvents_ConnectDetails { + if x != nil { + return x.ConnectTimes + } + return nil +} + var File_edge_ctrl_proto protoreflect.FileDescriptor var file_edge_ctrl_proto_rawDesc = []byte{ @@ -5348,13 +5540,15 @@ var file_edge_ctrl_proto_rawDesc = []byte{ 0x62, 0x74, 0x79, 0x70, 0x65, 0x22, 0x2c, 0x0a, 0x06, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x10, 0x02, 0x22, 0x5e, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x65, 0x10, 0x02, 0x22, 0x7e, 0x0a, 0x0a, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x2a, 0x0a, 0x10, 0x63, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x63, 0x65, 0x72, 0x74, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x02, 0x69, 0x64, 0x22, 0x74, 0x0a, 0x0f, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x02, 0x69, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x49, 0x64, 0x22, 0x74, 0x0a, 0x0f, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x3f, 0x0a, 0x0b, 0x61, 0x70, 0x69, 0x53, @@ -5853,144 +6047,171 @@ var file_edge_ctrl_proto_rawDesc = []byte{ 0x65, 0x72, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x50, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, - 0x72, 0x74, 0x50, 0x65, 0x6d, 0x2a, 0xca, 0x0c, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x65, 0x72, 0x6f, 0x10, 0x00, 0x12, - 0x15, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x79, - 0x70, 0x65, 0x10, 0xa0, 0x9c, 0x01, 0x12, 0x15, 0x0a, 0x0f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x10, 0xa1, 0x9c, 0x01, 0x12, 0x0f, 0x0a, - 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x54, 0x79, 0x70, 0x65, 0x10, 0xa2, 0x9c, 0x01, 0x12, 0x18, - 0x0a, 0x12, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, - 0x54, 0x79, 0x70, 0x65, 0x10, 0x86, 0x9d, 0x01, 0x12, 0x19, 0x0a, 0x13, 0x41, 0x70, 0x69, 0x53, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, 0x64, 0x64, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, - 0xe8, 0x9d, 0x01, 0x12, 0x1b, 0x0a, 0x15, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, - 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe9, 0x9d, 0x01, - 0x12, 0x1b, 0x0a, 0x15, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0xea, 0x9d, 0x01, 0x12, 0x1d, 0x0a, - 0x17, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x72, 0x74, - 0x62, 0x65, 0x61, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xeb, 0x9d, 0x01, 0x12, 0x1d, 0x0a, 0x17, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x53, - 0x79, 0x6e, 0x63, 0x54, 0x79, 0x70, 0x65, 0x10, 0xec, 0x9d, 0x01, 0x12, 0x1e, 0x0a, 0x18, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xed, 0x9d, 0x01, 0x12, 0x1f, 0x0a, 0x19, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xee, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xef, 0x9d, 0x01, 0x12, - 0x22, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, + 0x72, 0x74, 0x50, 0x65, 0x6d, 0x22, 0x96, 0x03, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x4e, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x7a, 0x69, 0x74, 0x69, 0x2e, 0x65, + 0x64, 0x67, 0x65, 0x5f, 0x63, 0x74, 0x72, 0x6c, 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, + 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x2e, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, + 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x66, 0x75, 0x6c, 0x6c, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x66, 0x75, 0x6c, 0x6c, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x66, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0b, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x72, 0x63, + 0x41, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x72, 0x63, 0x41, + 0x64, 0x64, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x64, 0x73, 0x74, 0x41, 0x64, 0x64, 0x72, 0x1a, 0xae, 0x01, + 0x0a, 0x15, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, + 0x69, 0x74, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x65, 0x64, 0x12, 0x53, 0x0a, 0x0c, 0x63, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x2f, 0x2e, 0x7a, 0x69, 0x74, 0x69, 0x2e, 0x65, 0x64, 0x67, 0x65, 0x5f, 0x63, 0x74, 0x72, 0x6c, + 0x2e, 0x70, 0x62, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x52, 0x0c, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x2a, 0xe4, + 0x0c, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, + 0x0a, 0x04, 0x5a, 0x65, 0x72, 0x6f, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x79, 0x70, 0x65, 0x10, 0xa0, 0x9c, 0x01, 0x12, + 0x15, 0x0a, 0x0f, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x54, 0x79, + 0x70, 0x65, 0x10, 0xa1, 0x9c, 0x01, 0x12, 0x0f, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x54, + 0x79, 0x70, 0x65, 0x10, 0xa2, 0x9c, 0x01, 0x12, 0x18, 0x0a, 0x12, 0x53, 0x65, 0x73, 0x73, 0x69, + 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0x86, 0x9d, + 0x01, 0x12, 0x19, 0x0a, 0x13, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x41, + 0x64, 0x64, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe8, 0x9d, 0x01, 0x12, 0x1b, 0x0a, 0x15, + 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe9, 0x9d, 0x01, 0x12, 0x1b, 0x0a, 0x15, 0x41, 0x70, 0x69, + 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x54, 0x79, + 0x70, 0x65, 0x10, 0xea, 0x9d, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x10, 0xeb, 0x9d, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x53, 0x79, 0x6e, 0x63, 0x54, 0x79, 0x70, 0x65, + 0x10, 0xec, 0x9d, 0x01, 0x12, 0x1e, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, + 0x72, 0x63, 0x75, 0x69, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x10, 0xed, 0x9d, 0x01, 0x12, 0x1f, 0x0a, 0x19, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, + 0x72, 0x63, 0x75, 0x69, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x10, 0xee, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x10, 0xef, 0x9d, 0x01, 0x12, 0x22, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf0, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf1, 0x9d, 0x01, 0x12, + 0x22, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, - 0xf0, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, + 0xf2, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x10, 0xf1, 0x9d, 0x01, 0x12, 0x22, 0x0a, 0x1c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x70, 0x65, 0x10, 0xf3, 0x9d, 0x01, 0x12, 0x22, 0x0a, 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf2, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x52, 0x65, - 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf3, 0x9d, 0x01, 0x12, 0x22, 0x0a, - 0x1c, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf4, 0x9d, - 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x10, 0xf5, 0x9d, 0x01, 0x12, 0x15, 0x0a, 0x0f, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf6, 0x9d, 0x01, 0x12, 0x23, 0x0a, 0x1d, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x56, - 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf8, 0x9d, 0x01, - 0x12, 0x24, 0x0a, 0x1e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x61, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, - 0x70, 0x65, 0x10, 0xf9, 0x9d, 0x01, 0x12, 0x20, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x10, 0xfa, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x56, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfb, 0x9d, 0x01, 0x12, 0x26, 0x0a, 0x20, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x69, 0x72, 0x63, 0x75, - 0x69, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, - 0xfc, 0x9d, 0x01, 0x12, 0x27, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, - 0x6e, 0x65, 0x6c, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x56, 0x32, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfd, 0x9d, 0x01, 0x12, 0x10, 0x0a, 0x0a, - 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x10, 0xcc, 0x9e, 0x01, 0x12, 0x21, - 0x0a, 0x1b, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xcd, 0x9e, - 0x01, 0x12, 0x27, 0x0a, 0x21, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x45, - 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xce, 0x9e, 0x01, 0x12, 0x2d, 0x0a, 0x27, 0x45, 0x6e, + 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf4, 0x9d, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf5, 0x9d, 0x01, 0x12, 0x15, 0x0a, + 0x0f, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x10, 0xf6, 0x9d, 0x01, 0x12, 0x23, 0x0a, 0x1d, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf8, 0x9d, 0x01, 0x12, 0x24, 0x0a, 0x1e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x56, 0x32, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf9, 0x9d, 0x01, 0x12, + 0x20, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, + 0x56, 0x32, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfa, 0x9d, + 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, + 0x69, 0x74, 0x56, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, + 0x10, 0xfb, 0x9d, 0x01, 0x12, 0x26, 0x0a, 0x20, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, + 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x56, 0x32, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfc, 0x9d, 0x01, 0x12, 0x27, 0x0a, 0x21, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x43, 0x69, 0x72, 0x63, + 0x75, 0x69, 0x74, 0x56, 0x32, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, + 0x65, 0x10, 0xfd, 0x9d, 0x01, 0x12, 0x10, 0x0a, 0x0a, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x54, + 0x79, 0x70, 0x65, 0x10, 0xcc, 0x9e, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x45, 0x6e, 0x72, 0x6f, 0x6c, + 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x43, 0x65, 0x72, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xcd, 0x9e, 0x01, 0x12, 0x27, 0x0a, 0x21, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xcf, 0x9e, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb0, 0x9f, 0x01, 0x12, 0x22, 0x0a, 0x1c, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb1, 0x9f, 0x01, - 0x12, 0x28, 0x0a, 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, - 0x74, 0x46, 0x6f, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb2, 0x9f, 0x01, 0x12, 0x29, 0x0a, 0x23, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x53, 0x65, - 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x10, 0xb3, 0x9f, 0x01, 0x12, 0x1d, 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, - 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x10, 0xb4, 0x9f, 0x01, 0x12, 0x15, 0x0a, 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, - 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb5, 0x9f, 0x01, 0x12, 0x27, 0x0a, 0x21, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x10, 0xb6, 0x9f, 0x01, 0x12, 0x28, 0x0a, 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, + 0x75, 0x74, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, + 0xce, 0x9e, 0x01, 0x12, 0x2d, 0x0a, 0x27, 0x45, 0x6e, 0x72, 0x6f, 0x6c, 0x6c, 0x6d, 0x65, 0x6e, + 0x74, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xcf, + 0x9e, 0x01, 0x12, 0x21, 0x0a, 0x1b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x53, + 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x10, 0xb0, 0x9f, 0x01, 0x12, 0x22, 0x0a, 0x1c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, + 0x70, 0x69, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb1, 0x9f, 0x01, 0x12, 0x28, 0x0a, 0x22, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, + 0xb2, 0x9f, 0x01, 0x12, 0x29, 0x0a, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x69, 0x72, + 0x63, 0x75, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb3, 0x9f, 0x01, 0x12, 0x1d, + 0x0a, 0x17, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb4, 0x9f, 0x01, 0x12, 0x15, 0x0a, + 0x0f, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x10, 0xb5, 0x9f, 0x01, 0x12, 0x27, 0x0a, 0x21, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb7, 0x9f, 0x01, 0x12, 0x27, - 0x0a, 0x21, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, - 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x10, 0xb8, 0x9f, 0x01, 0x12, 0x28, 0x0a, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb6, 0x9f, 0x01, 0x12, 0x28, 0x0a, + 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, + 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, + 0x79, 0x70, 0x65, 0x10, 0xb7, 0x9f, 0x01, 0x12, 0x27, 0x0a, 0x21, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, - 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb9, 0x9f, - 0x01, 0x12, 0x27, 0x0a, 0x21, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, - 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xba, 0x9f, 0x01, 0x12, 0x28, 0x0a, 0x22, 0x52, 0x65, + 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb8, 0x9f, 0x01, + 0x12, 0x28, 0x0a, 0x22, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, + 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb9, 0x9f, 0x01, 0x12, 0x27, 0x0a, 0x21, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, - 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x10, 0xbb, 0x9f, 0x01, 0x12, 0x1b, 0x0a, 0x15, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x48, 0x65, - 0x61, 0x6c, 0x74, 0x68, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbc, 0x9f, - 0x01, 0x12, 0x29, 0x0a, 0x23, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, - 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x56, 0x32, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbd, 0x9f, 0x01, 0x12, 0x2a, 0x0a, 0x24, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, - 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, - 0x54, 0x79, 0x70, 0x65, 0x10, 0xbe, 0x9f, 0x01, 0x12, 0x13, 0x0a, 0x0d, 0x44, 0x61, 0x74, 0x61, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0x94, 0xa0, 0x01, 0x12, 0x1c, 0x0a, - 0x16, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, - 0x53, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0x95, 0xa0, 0x01, 0x12, 0x15, 0x0a, 0x0f, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x10, 0x96, - 0xa0, 0x01, 0x2a, 0x21, 0x0a, 0x0b, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x69, 0x61, 0x6c, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x42, - 0x69, 0x6e, 0x64, 0x10, 0x01, 0x2a, 0x6e, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, - 0x0e, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5a, 0x65, 0x72, 0x6f, 0x10, 0x00, 0x12, - 0x11, 0x0a, 0x0c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x10, - 0xfe, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, 0x73, 0x73, 0x65, - 0x64, 0x10, 0xff, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x61, - 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x10, 0x80, 0x08, 0x12, 0x19, 0x0a, 0x14, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x49, 0x6e, 0x64, - 0x65, 0x78, 0x10, 0x81, 0x08, 0x2a, 0x3f, 0x0a, 0x0a, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x54, - 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x69, 0x61, 0x6c, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x42, 0x69, 0x6e, 0x64, 0x50, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x10, 0x02, 0x2a, 0x7a, 0x0a, 0x1e, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x45, 0x6e, - 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x49, 0x6e, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, 0x65, 0x10, 0x00, - 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x49, - 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x65, 0x6c, - 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x10, 0x03, 0x2a, 0x3d, 0x0a, 0x14, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, - 0x50, 0x72, 0x65, 0x63, 0x65, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x65, - 0x66, 0x61, 0x75, 0x6c, 0x74, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x71, 0x75, 0x69, - 0x72, 0x65, 0x64, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, - 0x02, 0x2a, 0x76, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0b, 0x0a, 0x07, 0x53, - 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x46, 0x61, 0x69, 0x6c, - 0x65, 0x64, 0x49, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x10, 0x01, 0x12, 0x0f, - 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x74, 0x68, 0x65, 0x72, 0x10, 0x02, 0x12, - 0x0e, 0x0a, 0x0a, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x75, 0x73, 0x79, 0x10, 0x03, 0x12, - 0x18, 0x0a, 0x14, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, - 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x42, 0x31, 0x5a, 0x2f, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x7a, 0x69, 0x74, 0x69, - 0x2f, 0x7a, 0x69, 0x74, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x70, 0x62, 0x2f, - 0x65, 0x64, 0x67, 0x65, 0x5f, 0x63, 0x74, 0x72, 0x6c, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, + 0xba, 0x9f, 0x01, 0x12, 0x28, 0x0a, 0x22, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x54, 0x75, 0x6e, + 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbb, 0x9f, 0x01, 0x12, 0x1b, 0x0a, + 0x15, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x48, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbc, 0x9f, 0x01, 0x12, 0x29, 0x0a, 0x23, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x54, 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, + 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x56, 0x32, 0x54, 0x79, 0x70, + 0x65, 0x10, 0xbd, 0x9f, 0x01, 0x12, 0x2a, 0x0a, 0x24, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, + 0x75, 0x6e, 0x6e, 0x65, 0x6c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x56, 0x32, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbe, 0x9f, + 0x01, 0x12, 0x18, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x54, 0x79, 0x70, 0x65, 0x73, 0x10, 0xbf, 0x9f, 0x01, 0x12, 0x13, 0x0a, 0x0d, 0x44, + 0x61, 0x74, 0x61, 0x53, 0x74, 0x61, 0x74, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0x94, 0xa0, 0x01, + 0x12, 0x1c, 0x0a, 0x16, 0x44, 0x61, 0x74, 0x61, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x68, 0x61, + 0x6e, 0x67, 0x65, 0x53, 0x65, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0x95, 0xa0, 0x01, 0x12, 0x15, + 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x54, 0x79, 0x70, + 0x65, 0x10, 0x96, 0xa0, 0x01, 0x2a, 0x21, 0x0a, 0x0b, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x69, 0x61, 0x6c, 0x10, 0x00, 0x12, 0x08, + 0x0a, 0x04, 0x42, 0x69, 0x6e, 0x64, 0x10, 0x01, 0x2a, 0x6e, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x5a, 0x65, 0x72, 0x6f, + 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0c, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, + 0x49, 0x64, 0x10, 0xfe, 0x07, 0x12, 0x10, 0x0a, 0x0b, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x50, 0x61, + 0x73, 0x73, 0x65, 0x64, 0x10, 0xff, 0x07, 0x12, 0x14, 0x0a, 0x0f, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x10, 0x80, 0x08, 0x12, 0x19, 0x0a, + 0x14, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x10, 0x81, 0x08, 0x2a, 0x3f, 0x0a, 0x0a, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x11, 0x0a, 0x0d, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x69, 0x61, + 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x42, 0x69, 0x6e, + 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x10, 0x02, 0x2a, 0x7a, 0x0a, 0x1e, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, + 0x64, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x49, + 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x54, 0x79, 0x70, + 0x65, 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x53, 0x65, + 0x72, 0x76, 0x69, 0x63, 0x65, 0x10, 0x01, 0x12, 0x13, 0x0a, 0x0f, 0x52, 0x65, 0x6c, 0x61, 0x74, + 0x65, 0x64, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x10, 0x02, 0x12, 0x17, 0x0a, 0x13, + 0x52, 0x65, 0x6c, 0x61, 0x74, 0x65, 0x64, 0x50, 0x6f, 0x73, 0x74, 0x75, 0x72, 0x65, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x10, 0x03, 0x2a, 0x3d, 0x0a, 0x14, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, + 0x74, 0x6f, 0x72, 0x50, 0x72, 0x65, 0x63, 0x65, 0x64, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x0b, 0x0a, + 0x07, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x46, 0x61, 0x69, 0x6c, + 0x65, 0x64, 0x10, 0x02, 0x2a, 0x76, 0x0a, 0x16, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x0b, + 0x0a, 0x07, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x10, 0x00, 0x12, 0x14, 0x0a, 0x10, 0x46, + 0x61, 0x69, 0x6c, 0x65, 0x64, 0x49, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x6c, 0x69, 0x63, 0x74, 0x10, + 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4f, 0x74, 0x68, 0x65, 0x72, + 0x10, 0x02, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x42, 0x75, 0x73, 0x79, + 0x10, 0x03, 0x12, 0x18, 0x0a, 0x14, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x49, 0x6e, 0x76, 0x61, + 0x6c, 0x69, 0x64, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x10, 0x04, 0x42, 0x31, 0x5a, 0x2f, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x7a, + 0x69, 0x74, 0x69, 0x2f, 0x7a, 0x69, 0x74, 0x69, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, + 0x70, 0x62, 0x2f, 0x65, 0x64, 0x67, 0x65, 0x5f, 0x63, 0x74, 0x72, 0x6c, 0x5f, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -6006,7 +6227,7 @@ func file_edge_ctrl_proto_rawDescGZIP() []byte { } var file_edge_ctrl_proto_enumTypes = make([]protoimpl.EnumInfo, 10) -var file_edge_ctrl_proto_msgTypes = make([]protoimpl.MessageInfo, 82) +var file_edge_ctrl_proto_msgTypes = make([]protoimpl.MessageInfo, 85) var file_edge_ctrl_proto_goTypes = []interface{}{ (ContentType)(0), // 0: ziti.edge_ctrl.pb.ContentType (SessionType)(0), // 1: ziti.edge_ctrl.pb.SessionType @@ -6059,126 +6280,131 @@ var file_edge_ctrl_proto_goTypes = []interface{}{ (*EnrollmentExtendRouterRequest)(nil), // 48: ziti.edge_ctrl.pb.EnrollmentExtendRouterRequest (*EnrollmentCertsResponse)(nil), // 49: ziti.edge_ctrl.pb.EnrollmentCertsResponse (*EnrollmentExtendRouterVerifyRequest)(nil), // 50: ziti.edge_ctrl.pb.EnrollmentExtendRouterVerifyRequest - nil, // 51: ziti.edge_ctrl.pb.ServerHello.DataEntry - nil, // 52: ziti.edge_ctrl.pb.ServerHello.ByteDataEntry - nil, // 53: ziti.edge_ctrl.pb.ClientHello.DataEntry - (*DataState_ConfigType)(nil), // 54: ziti.edge_ctrl.pb.DataState.ConfigType - (*DataState_Config)(nil), // 55: ziti.edge_ctrl.pb.DataState.Config - (*DataState_ServiceConfigs)(nil), // 56: ziti.edge_ctrl.pb.DataState.ServiceConfigs - (*DataState_Identity)(nil), // 57: ziti.edge_ctrl.pb.DataState.Identity - (*DataState_Service)(nil), // 58: ziti.edge_ctrl.pb.DataState.Service - (*DataState_ServicePolicy)(nil), // 59: ziti.edge_ctrl.pb.DataState.ServicePolicy - (*DataState_Revocation)(nil), // 60: ziti.edge_ctrl.pb.DataState.Revocation - (*DataState_ServicePolicyChange)(nil), // 61: ziti.edge_ctrl.pb.DataState.ServicePolicyChange - (*DataState_ChangeSet)(nil), // 62: ziti.edge_ctrl.pb.DataState.ChangeSet - (*DataState_Event)(nil), // 63: ziti.edge_ctrl.pb.DataState.Event - (*DataState_PublicKey)(nil), // 64: ziti.edge_ctrl.pb.DataState.PublicKey - (*DataState_PostureCheck)(nil), // 65: ziti.edge_ctrl.pb.DataState.PostureCheck - nil, // 66: ziti.edge_ctrl.pb.DataState.ServiceConfigs.ConfigsEntry - nil, // 67: ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingPrecedencesEntry - nil, // 68: ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingCostsEntry - nil, // 69: ziti.edge_ctrl.pb.DataState.Identity.ServiceConfigsEntry - (*DataState_PostureCheck_Mac)(nil), // 70: ziti.edge_ctrl.pb.DataState.PostureCheck.Mac - (*DataState_PostureCheck_Mfa)(nil), // 71: ziti.edge_ctrl.pb.DataState.PostureCheck.Mfa - (*DataState_PostureCheck_Os)(nil), // 72: ziti.edge_ctrl.pb.DataState.PostureCheck.Os - (*DataState_PostureCheck_OsList)(nil), // 73: ziti.edge_ctrl.pb.DataState.PostureCheck.OsList - (*DataState_PostureCheck_Process)(nil), // 74: ziti.edge_ctrl.pb.DataState.PostureCheck.Process - (*DataState_PostureCheck_ProcessMulti)(nil), // 75: ziti.edge_ctrl.pb.DataState.PostureCheck.ProcessMulti - (*DataState_PostureCheck_Domains)(nil), // 76: ziti.edge_ctrl.pb.DataState.PostureCheck.Domains - nil, // 77: ziti.edge_ctrl.pb.CreateCircuitRequest.PeerDataEntry - nil, // 78: ziti.edge_ctrl.pb.CreateCircuitResponse.PeerDataEntry - nil, // 79: ziti.edge_ctrl.pb.CreateCircuitResponse.TagsEntry - nil, // 80: ziti.edge_ctrl.pb.CreateTerminatorRequest.PeerDataEntry - nil, // 81: ziti.edge_ctrl.pb.CreateTerminatorV2Request.PeerDataEntry - nil, // 82: ziti.edge_ctrl.pb.CreateApiSessionResponse.ServicePrecedencesEntry - nil, // 83: ziti.edge_ctrl.pb.CreateApiSessionResponse.ServiceCostsEntry - nil, // 84: ziti.edge_ctrl.pb.CreateCircuitForServiceRequest.PeerDataEntry - nil, // 85: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.PeerDataEntry - nil, // 86: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.TagsEntry - nil, // 87: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Request.PeerDataEntry - nil, // 88: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.PeerDataEntry - nil, // 89: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.TagsEntry - nil, // 90: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequest.PeerDataEntry - nil, // 91: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequestV2.PeerDataEntry - (*timestamppb.Timestamp)(nil), // 92: google.protobuf.Timestamp + (*ConnectEvents)(nil), // 51: ziti.edge_ctrl.pb.ConnectEvents + nil, // 52: ziti.edge_ctrl.pb.ServerHello.DataEntry + nil, // 53: ziti.edge_ctrl.pb.ServerHello.ByteDataEntry + nil, // 54: ziti.edge_ctrl.pb.ClientHello.DataEntry + (*DataState_ConfigType)(nil), // 55: ziti.edge_ctrl.pb.DataState.ConfigType + (*DataState_Config)(nil), // 56: ziti.edge_ctrl.pb.DataState.Config + (*DataState_ServiceConfigs)(nil), // 57: ziti.edge_ctrl.pb.DataState.ServiceConfigs + (*DataState_Identity)(nil), // 58: ziti.edge_ctrl.pb.DataState.Identity + (*DataState_Service)(nil), // 59: ziti.edge_ctrl.pb.DataState.Service + (*DataState_ServicePolicy)(nil), // 60: ziti.edge_ctrl.pb.DataState.ServicePolicy + (*DataState_Revocation)(nil), // 61: ziti.edge_ctrl.pb.DataState.Revocation + (*DataState_ServicePolicyChange)(nil), // 62: ziti.edge_ctrl.pb.DataState.ServicePolicyChange + (*DataState_ChangeSet)(nil), // 63: ziti.edge_ctrl.pb.DataState.ChangeSet + (*DataState_Event)(nil), // 64: ziti.edge_ctrl.pb.DataState.Event + (*DataState_PublicKey)(nil), // 65: ziti.edge_ctrl.pb.DataState.PublicKey + (*DataState_PostureCheck)(nil), // 66: ziti.edge_ctrl.pb.DataState.PostureCheck + nil, // 67: ziti.edge_ctrl.pb.DataState.ServiceConfigs.ConfigsEntry + nil, // 68: ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingPrecedencesEntry + nil, // 69: ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingCostsEntry + nil, // 70: ziti.edge_ctrl.pb.DataState.Identity.ServiceConfigsEntry + (*DataState_PostureCheck_Mac)(nil), // 71: ziti.edge_ctrl.pb.DataState.PostureCheck.Mac + (*DataState_PostureCheck_Mfa)(nil), // 72: ziti.edge_ctrl.pb.DataState.PostureCheck.Mfa + (*DataState_PostureCheck_Os)(nil), // 73: ziti.edge_ctrl.pb.DataState.PostureCheck.Os + (*DataState_PostureCheck_OsList)(nil), // 74: ziti.edge_ctrl.pb.DataState.PostureCheck.OsList + (*DataState_PostureCheck_Process)(nil), // 75: ziti.edge_ctrl.pb.DataState.PostureCheck.Process + (*DataState_PostureCheck_ProcessMulti)(nil), // 76: ziti.edge_ctrl.pb.DataState.PostureCheck.ProcessMulti + (*DataState_PostureCheck_Domains)(nil), // 77: ziti.edge_ctrl.pb.DataState.PostureCheck.Domains + nil, // 78: ziti.edge_ctrl.pb.CreateCircuitRequest.PeerDataEntry + nil, // 79: ziti.edge_ctrl.pb.CreateCircuitResponse.PeerDataEntry + nil, // 80: ziti.edge_ctrl.pb.CreateCircuitResponse.TagsEntry + nil, // 81: ziti.edge_ctrl.pb.CreateTerminatorRequest.PeerDataEntry + nil, // 82: ziti.edge_ctrl.pb.CreateTerminatorV2Request.PeerDataEntry + nil, // 83: ziti.edge_ctrl.pb.CreateApiSessionResponse.ServicePrecedencesEntry + nil, // 84: ziti.edge_ctrl.pb.CreateApiSessionResponse.ServiceCostsEntry + nil, // 85: ziti.edge_ctrl.pb.CreateCircuitForServiceRequest.PeerDataEntry + nil, // 86: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.PeerDataEntry + nil, // 87: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.TagsEntry + nil, // 88: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Request.PeerDataEntry + nil, // 89: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.PeerDataEntry + nil, // 90: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.TagsEntry + nil, // 91: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequest.PeerDataEntry + nil, // 92: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequestV2.PeerDataEntry + (*ConnectEvents_ConnectDetails)(nil), // 93: ziti.edge_ctrl.pb.ConnectEvents.ConnectDetails + (*ConnectEvents_IdentityConnectEvents)(nil), // 94: ziti.edge_ctrl.pb.ConnectEvents.IdentityConnectEvents + (*timestamppb.Timestamp)(nil), // 95: google.protobuf.Timestamp } var file_edge_ctrl_proto_depIdxs = []int32{ - 51, // 0: ziti.edge_ctrl.pb.ServerHello.data:type_name -> ziti.edge_ctrl.pb.ServerHello.DataEntry - 52, // 1: ziti.edge_ctrl.pb.ServerHello.byteData:type_name -> ziti.edge_ctrl.pb.ServerHello.ByteDataEntry + 52, // 0: ziti.edge_ctrl.pb.ServerHello.data:type_name -> ziti.edge_ctrl.pb.ServerHello.DataEntry + 53, // 1: ziti.edge_ctrl.pb.ServerHello.byteData:type_name -> ziti.edge_ctrl.pb.ServerHello.ByteDataEntry 11, // 2: ziti.edge_ctrl.pb.Listener.address:type_name -> ziti.edge_ctrl.pb.Address 11, // 3: ziti.edge_ctrl.pb.Listener.advertise:type_name -> ziti.edge_ctrl.pb.Address - 53, // 4: ziti.edge_ctrl.pb.ClientHello.data:type_name -> ziti.edge_ctrl.pb.ClientHello.DataEntry + 54, // 4: ziti.edge_ctrl.pb.ClientHello.data:type_name -> ziti.edge_ctrl.pb.ClientHello.DataEntry 12, // 5: ziti.edge_ctrl.pb.ClientHello.listeners:type_name -> ziti.edge_ctrl.pb.Listener - 63, // 6: ziti.edge_ctrl.pb.DataState.events:type_name -> ziti.edge_ctrl.pb.DataState.Event + 64, // 6: ziti.edge_ctrl.pb.DataState.events:type_name -> ziti.edge_ctrl.pb.DataState.Event 16, // 7: ziti.edge_ctrl.pb.ApiSessionAdded.apiSessions:type_name -> ziti.edge_ctrl.pb.ApiSession 16, // 8: ziti.edge_ctrl.pb.ApiSessionUpdated.apiSessions:type_name -> ziti.edge_ctrl.pb.ApiSession - 77, // 9: ziti.edge_ctrl.pb.CreateCircuitRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitRequest.PeerDataEntry - 78, // 10: ziti.edge_ctrl.pb.CreateCircuitResponse.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitResponse.PeerDataEntry - 79, // 11: ziti.edge_ctrl.pb.CreateCircuitResponse.tags:type_name -> ziti.edge_ctrl.pb.CreateCircuitResponse.TagsEntry - 80, // 12: ziti.edge_ctrl.pb.CreateTerminatorRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateTerminatorRequest.PeerDataEntry + 78, // 9: ziti.edge_ctrl.pb.CreateCircuitRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitRequest.PeerDataEntry + 79, // 10: ziti.edge_ctrl.pb.CreateCircuitResponse.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitResponse.PeerDataEntry + 80, // 11: ziti.edge_ctrl.pb.CreateCircuitResponse.tags:type_name -> ziti.edge_ctrl.pb.CreateCircuitResponse.TagsEntry + 81, // 12: ziti.edge_ctrl.pb.CreateTerminatorRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateTerminatorRequest.PeerDataEntry 5, // 13: ziti.edge_ctrl.pb.CreateTerminatorRequest.precedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence - 81, // 14: ziti.edge_ctrl.pb.CreateTerminatorV2Request.peerData:type_name -> ziti.edge_ctrl.pb.CreateTerminatorV2Request.PeerDataEntry + 82, // 14: ziti.edge_ctrl.pb.CreateTerminatorV2Request.peerData:type_name -> ziti.edge_ctrl.pb.CreateTerminatorV2Request.PeerDataEntry 5, // 15: ziti.edge_ctrl.pb.CreateTerminatorV2Request.precedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence 6, // 16: ziti.edge_ctrl.pb.CreateTerminatorV2Response.result:type_name -> ziti.edge_ctrl.pb.CreateTerminatorResult 5, // 17: ziti.edge_ctrl.pb.UpdateTerminatorRequest.precedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence 32, // 18: ziti.edge_ctrl.pb.CreateApiSessionRequest.envInfo:type_name -> ziti.edge_ctrl.pb.EnvInfo 33, // 19: ziti.edge_ctrl.pb.CreateApiSessionRequest.sdkInfo:type_name -> ziti.edge_ctrl.pb.SdkInfo 5, // 20: ziti.edge_ctrl.pb.CreateApiSessionResponse.defaultHostingPrecedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence - 82, // 21: ziti.edge_ctrl.pb.CreateApiSessionResponse.servicePrecedences:type_name -> ziti.edge_ctrl.pb.CreateApiSessionResponse.ServicePrecedencesEntry - 83, // 22: ziti.edge_ctrl.pb.CreateApiSessionResponse.serviceCosts:type_name -> ziti.edge_ctrl.pb.CreateApiSessionResponse.ServiceCostsEntry - 84, // 23: ziti.edge_ctrl.pb.CreateCircuitForServiceRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitForServiceRequest.PeerDataEntry + 83, // 21: ziti.edge_ctrl.pb.CreateApiSessionResponse.servicePrecedences:type_name -> ziti.edge_ctrl.pb.CreateApiSessionResponse.ServicePrecedencesEntry + 84, // 22: ziti.edge_ctrl.pb.CreateApiSessionResponse.serviceCosts:type_name -> ziti.edge_ctrl.pb.CreateApiSessionResponse.ServiceCostsEntry + 85, // 23: ziti.edge_ctrl.pb.CreateCircuitForServiceRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitForServiceRequest.PeerDataEntry 35, // 24: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.apiSession:type_name -> ziti.edge_ctrl.pb.CreateApiSessionResponse 37, // 25: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.session:type_name -> ziti.edge_ctrl.pb.CreateSessionResponse - 85, // 26: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.PeerDataEntry - 86, // 27: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.tags:type_name -> ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.TagsEntry - 87, // 28: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Request.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelCircuitV2Request.PeerDataEntry - 88, // 29: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.PeerDataEntry - 89, // 30: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.tags:type_name -> ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.TagsEntry + 86, // 26: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.peerData:type_name -> ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.PeerDataEntry + 87, // 27: ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.tags:type_name -> ziti.edge_ctrl.pb.CreateCircuitForServiceResponse.TagsEntry + 88, // 28: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Request.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelCircuitV2Request.PeerDataEntry + 89, // 29: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.PeerDataEntry + 90, // 30: ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.tags:type_name -> ziti.edge_ctrl.pb.CreateTunnelCircuitV2Response.TagsEntry 42, // 31: ziti.edge_ctrl.pb.ServicesList.services:type_name -> ziti.edge_ctrl.pb.TunnelService - 90, // 32: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelTerminatorRequest.PeerDataEntry + 91, // 32: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequest.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelTerminatorRequest.PeerDataEntry 5, // 33: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequest.precedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence 35, // 34: ziti.edge_ctrl.pb.CreateTunnelTerminatorResponse.apiSession:type_name -> ziti.edge_ctrl.pb.CreateApiSessionResponse 37, // 35: ziti.edge_ctrl.pb.CreateTunnelTerminatorResponse.session:type_name -> ziti.edge_ctrl.pb.CreateSessionResponse - 91, // 36: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequestV2.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelTerminatorRequestV2.PeerDataEntry + 92, // 36: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequestV2.peerData:type_name -> ziti.edge_ctrl.pb.CreateTunnelTerminatorRequestV2.PeerDataEntry 5, // 37: ziti.edge_ctrl.pb.CreateTunnelTerminatorRequestV2.precedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence 5, // 38: ziti.edge_ctrl.pb.UpdateTunnelTerminatorRequest.precedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence - 66, // 39: ziti.edge_ctrl.pb.DataState.ServiceConfigs.configs:type_name -> ziti.edge_ctrl.pb.DataState.ServiceConfigs.ConfigsEntry - 5, // 40: ziti.edge_ctrl.pb.DataState.Identity.defaultHostingPrecedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence - 67, // 41: ziti.edge_ctrl.pb.DataState.Identity.serviceHostingPrecedences:type_name -> ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingPrecedencesEntry - 68, // 42: ziti.edge_ctrl.pb.DataState.Identity.serviceHostingCosts:type_name -> ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingCostsEntry - 69, // 43: ziti.edge_ctrl.pb.DataState.Identity.serviceConfigs:type_name -> ziti.edge_ctrl.pb.DataState.Identity.ServiceConfigsEntry - 3, // 44: ziti.edge_ctrl.pb.DataState.ServicePolicy.policyType:type_name -> ziti.edge_ctrl.pb.PolicyType - 92, // 45: ziti.edge_ctrl.pb.DataState.Revocation.ExpiresAt:type_name -> google.protobuf.Timestamp - 4, // 46: ziti.edge_ctrl.pb.DataState.ServicePolicyChange.relatedEntityType:type_name -> ziti.edge_ctrl.pb.ServicePolicyRelatedEntityType - 63, // 47: ziti.edge_ctrl.pb.DataState.ChangeSet.changes:type_name -> ziti.edge_ctrl.pb.DataState.Event - 7, // 48: ziti.edge_ctrl.pb.DataState.Event.action:type_name -> ziti.edge_ctrl.pb.DataState.Action - 57, // 49: ziti.edge_ctrl.pb.DataState.Event.identity:type_name -> ziti.edge_ctrl.pb.DataState.Identity - 58, // 50: ziti.edge_ctrl.pb.DataState.Event.service:type_name -> ziti.edge_ctrl.pb.DataState.Service - 59, // 51: ziti.edge_ctrl.pb.DataState.Event.servicePolicy:type_name -> ziti.edge_ctrl.pb.DataState.ServicePolicy - 65, // 52: ziti.edge_ctrl.pb.DataState.Event.postureCheck:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck - 64, // 53: ziti.edge_ctrl.pb.DataState.Event.publicKey:type_name -> ziti.edge_ctrl.pb.DataState.PublicKey - 60, // 54: ziti.edge_ctrl.pb.DataState.Event.revocation:type_name -> ziti.edge_ctrl.pb.DataState.Revocation - 61, // 55: ziti.edge_ctrl.pb.DataState.Event.servicePolicyChange:type_name -> ziti.edge_ctrl.pb.DataState.ServicePolicyChange - 54, // 56: ziti.edge_ctrl.pb.DataState.Event.configType:type_name -> ziti.edge_ctrl.pb.DataState.ConfigType - 55, // 57: ziti.edge_ctrl.pb.DataState.Event.config:type_name -> ziti.edge_ctrl.pb.DataState.Config - 8, // 58: ziti.edge_ctrl.pb.DataState.PublicKey.usages:type_name -> ziti.edge_ctrl.pb.DataState.PublicKey.Usage - 9, // 59: ziti.edge_ctrl.pb.DataState.PublicKey.format:type_name -> ziti.edge_ctrl.pb.DataState.PublicKey.Format - 70, // 60: ziti.edge_ctrl.pb.DataState.PostureCheck.mac:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Mac - 71, // 61: ziti.edge_ctrl.pb.DataState.PostureCheck.mfa:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Mfa - 73, // 62: ziti.edge_ctrl.pb.DataState.PostureCheck.osList:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.OsList - 74, // 63: ziti.edge_ctrl.pb.DataState.PostureCheck.process:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Process - 75, // 64: ziti.edge_ctrl.pb.DataState.PostureCheck.processMulti:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.ProcessMulti - 76, // 65: ziti.edge_ctrl.pb.DataState.PostureCheck.domains:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Domains - 5, // 66: ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingPrecedencesEntry.value:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence - 56, // 67: ziti.edge_ctrl.pb.DataState.Identity.ServiceConfigsEntry.value:type_name -> ziti.edge_ctrl.pb.DataState.ServiceConfigs - 72, // 68: ziti.edge_ctrl.pb.DataState.PostureCheck.OsList.osList:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Os - 74, // 69: ziti.edge_ctrl.pb.DataState.PostureCheck.ProcessMulti.processes:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Process - 5, // 70: ziti.edge_ctrl.pb.CreateApiSessionResponse.ServicePrecedencesEntry.value:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence - 71, // [71:71] is the sub-list for method output_type - 71, // [71:71] is the sub-list for method input_type - 71, // [71:71] is the sub-list for extension type_name - 71, // [71:71] is the sub-list for extension extendee - 0, // [0:71] is the sub-list for field type_name + 94, // 39: ziti.edge_ctrl.pb.ConnectEvents.events:type_name -> ziti.edge_ctrl.pb.ConnectEvents.IdentityConnectEvents + 67, // 40: ziti.edge_ctrl.pb.DataState.ServiceConfigs.configs:type_name -> ziti.edge_ctrl.pb.DataState.ServiceConfigs.ConfigsEntry + 5, // 41: ziti.edge_ctrl.pb.DataState.Identity.defaultHostingPrecedence:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence + 68, // 42: ziti.edge_ctrl.pb.DataState.Identity.serviceHostingPrecedences:type_name -> ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingPrecedencesEntry + 69, // 43: ziti.edge_ctrl.pb.DataState.Identity.serviceHostingCosts:type_name -> ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingCostsEntry + 70, // 44: ziti.edge_ctrl.pb.DataState.Identity.serviceConfigs:type_name -> ziti.edge_ctrl.pb.DataState.Identity.ServiceConfigsEntry + 3, // 45: ziti.edge_ctrl.pb.DataState.ServicePolicy.policyType:type_name -> ziti.edge_ctrl.pb.PolicyType + 95, // 46: ziti.edge_ctrl.pb.DataState.Revocation.ExpiresAt:type_name -> google.protobuf.Timestamp + 4, // 47: ziti.edge_ctrl.pb.DataState.ServicePolicyChange.relatedEntityType:type_name -> ziti.edge_ctrl.pb.ServicePolicyRelatedEntityType + 64, // 48: ziti.edge_ctrl.pb.DataState.ChangeSet.changes:type_name -> ziti.edge_ctrl.pb.DataState.Event + 7, // 49: ziti.edge_ctrl.pb.DataState.Event.action:type_name -> ziti.edge_ctrl.pb.DataState.Action + 58, // 50: ziti.edge_ctrl.pb.DataState.Event.identity:type_name -> ziti.edge_ctrl.pb.DataState.Identity + 59, // 51: ziti.edge_ctrl.pb.DataState.Event.service:type_name -> ziti.edge_ctrl.pb.DataState.Service + 60, // 52: ziti.edge_ctrl.pb.DataState.Event.servicePolicy:type_name -> ziti.edge_ctrl.pb.DataState.ServicePolicy + 66, // 53: ziti.edge_ctrl.pb.DataState.Event.postureCheck:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck + 65, // 54: ziti.edge_ctrl.pb.DataState.Event.publicKey:type_name -> ziti.edge_ctrl.pb.DataState.PublicKey + 61, // 55: ziti.edge_ctrl.pb.DataState.Event.revocation:type_name -> ziti.edge_ctrl.pb.DataState.Revocation + 62, // 56: ziti.edge_ctrl.pb.DataState.Event.servicePolicyChange:type_name -> ziti.edge_ctrl.pb.DataState.ServicePolicyChange + 55, // 57: ziti.edge_ctrl.pb.DataState.Event.configType:type_name -> ziti.edge_ctrl.pb.DataState.ConfigType + 56, // 58: ziti.edge_ctrl.pb.DataState.Event.config:type_name -> ziti.edge_ctrl.pb.DataState.Config + 8, // 59: ziti.edge_ctrl.pb.DataState.PublicKey.usages:type_name -> ziti.edge_ctrl.pb.DataState.PublicKey.Usage + 9, // 60: ziti.edge_ctrl.pb.DataState.PublicKey.format:type_name -> ziti.edge_ctrl.pb.DataState.PublicKey.Format + 71, // 61: ziti.edge_ctrl.pb.DataState.PostureCheck.mac:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Mac + 72, // 62: ziti.edge_ctrl.pb.DataState.PostureCheck.mfa:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Mfa + 74, // 63: ziti.edge_ctrl.pb.DataState.PostureCheck.osList:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.OsList + 75, // 64: ziti.edge_ctrl.pb.DataState.PostureCheck.process:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Process + 76, // 65: ziti.edge_ctrl.pb.DataState.PostureCheck.processMulti:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.ProcessMulti + 77, // 66: ziti.edge_ctrl.pb.DataState.PostureCheck.domains:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Domains + 5, // 67: ziti.edge_ctrl.pb.DataState.Identity.ServiceHostingPrecedencesEntry.value:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence + 57, // 68: ziti.edge_ctrl.pb.DataState.Identity.ServiceConfigsEntry.value:type_name -> ziti.edge_ctrl.pb.DataState.ServiceConfigs + 73, // 69: ziti.edge_ctrl.pb.DataState.PostureCheck.OsList.osList:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Os + 75, // 70: ziti.edge_ctrl.pb.DataState.PostureCheck.ProcessMulti.processes:type_name -> ziti.edge_ctrl.pb.DataState.PostureCheck.Process + 5, // 71: ziti.edge_ctrl.pb.CreateApiSessionResponse.ServicePrecedencesEntry.value:type_name -> ziti.edge_ctrl.pb.TerminatorPrecedence + 93, // 72: ziti.edge_ctrl.pb.ConnectEvents.IdentityConnectEvents.connectTimes:type_name -> ziti.edge_ctrl.pb.ConnectEvents.ConnectDetails + 73, // [73:73] is the sub-list for method output_type + 73, // [73:73] is the sub-list for method input_type + 73, // [73:73] is the sub-list for extension type_name + 73, // [73:73] is the sub-list for extension extendee + 0, // [0:73] is the sub-list for field type_name } func init() { file_edge_ctrl_proto_init() } @@ -6679,8 +6905,8 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[44].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_ConfigType); i { + file_edge_ctrl_proto_msgTypes[41].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectEvents); i { case 0: return &v.state case 1: @@ -6692,7 +6918,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[45].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_Config); i { + switch v := v.(*DataState_ConfigType); i { case 0: return &v.state case 1: @@ -6704,7 +6930,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[46].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_ServiceConfigs); i { + switch v := v.(*DataState_Config); i { case 0: return &v.state case 1: @@ -6716,7 +6942,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[47].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_Identity); i { + switch v := v.(*DataState_ServiceConfigs); i { case 0: return &v.state case 1: @@ -6728,7 +6954,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[48].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_Service); i { + switch v := v.(*DataState_Identity); i { case 0: return &v.state case 1: @@ -6740,7 +6966,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[49].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_ServicePolicy); i { + switch v := v.(*DataState_Service); i { case 0: return &v.state case 1: @@ -6752,7 +6978,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[50].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_Revocation); i { + switch v := v.(*DataState_ServicePolicy); i { case 0: return &v.state case 1: @@ -6764,7 +6990,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[51].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_ServicePolicyChange); i { + switch v := v.(*DataState_Revocation); i { case 0: return &v.state case 1: @@ -6776,7 +7002,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[52].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_ChangeSet); i { + switch v := v.(*DataState_ServicePolicyChange); i { case 0: return &v.state case 1: @@ -6788,7 +7014,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[53].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_Event); i { + switch v := v.(*DataState_ChangeSet); i { case 0: return &v.state case 1: @@ -6800,7 +7026,7 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[54].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DataState_PublicKey); i { + switch v := v.(*DataState_Event); i { case 0: return &v.state case 1: @@ -6812,6 +7038,18 @@ func file_edge_ctrl_proto_init() { } } file_edge_ctrl_proto_msgTypes[55].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DataState_PublicKey); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_edge_ctrl_proto_msgTypes[56].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck); i { case 0: return &v.state @@ -6823,7 +7061,7 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[60].Exporter = func(v interface{}, i int) interface{} { + file_edge_ctrl_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck_Mac); i { case 0: return &v.state @@ -6835,7 +7073,7 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[61].Exporter = func(v interface{}, i int) interface{} { + file_edge_ctrl_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck_Mfa); i { case 0: return &v.state @@ -6847,7 +7085,7 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[62].Exporter = func(v interface{}, i int) interface{} { + file_edge_ctrl_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck_Os); i { case 0: return &v.state @@ -6859,7 +7097,7 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[63].Exporter = func(v interface{}, i int) interface{} { + file_edge_ctrl_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck_OsList); i { case 0: return &v.state @@ -6871,7 +7109,7 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[64].Exporter = func(v interface{}, i int) interface{} { + file_edge_ctrl_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck_Process); i { case 0: return &v.state @@ -6883,7 +7121,7 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[65].Exporter = func(v interface{}, i int) interface{} { + file_edge_ctrl_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck_ProcessMulti); i { case 0: return &v.state @@ -6895,7 +7133,7 @@ func file_edge_ctrl_proto_init() { return nil } } - file_edge_ctrl_proto_msgTypes[66].Exporter = func(v interface{}, i int) interface{} { + file_edge_ctrl_proto_msgTypes[67].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DataState_PostureCheck_Domains); i { case 0: return &v.state @@ -6907,8 +7145,32 @@ func file_edge_ctrl_proto_init() { return nil } } + file_edge_ctrl_proto_msgTypes[83].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectEvents_ConnectDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_edge_ctrl_proto_msgTypes[84].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ConnectEvents_IdentityConnectEvents); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } - file_edge_ctrl_proto_msgTypes[53].OneofWrappers = []interface{}{ + file_edge_ctrl_proto_msgTypes[54].OneofWrappers = []interface{}{ (*DataState_Event_Identity)(nil), (*DataState_Event_Service)(nil), (*DataState_Event_ServicePolicy)(nil), @@ -6919,7 +7181,7 @@ func file_edge_ctrl_proto_init() { (*DataState_Event_ConfigType)(nil), (*DataState_Event_Config)(nil), } - file_edge_ctrl_proto_msgTypes[55].OneofWrappers = []interface{}{ + file_edge_ctrl_proto_msgTypes[56].OneofWrappers = []interface{}{ (*DataState_PostureCheck_Mac_)(nil), (*DataState_PostureCheck_Mfa_)(nil), (*DataState_PostureCheck_OsList_)(nil), @@ -6933,7 +7195,7 @@ func file_edge_ctrl_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_edge_ctrl_proto_rawDesc, NumEnums: 10, - NumMessages: 82, + NumMessages: 85, NumExtensions: 0, NumServices: 0, }, diff --git a/common/pb/edge_ctrl_pb/edge_ctrl.proto b/common/pb/edge_ctrl_pb/edge_ctrl.proto index e0e4831ae..0228524fa 100644 --- a/common/pb/edge_ctrl_pb/edge_ctrl.proto +++ b/common/pb/edge_ctrl_pb/edge_ctrl.proto @@ -60,6 +60,7 @@ enum ContentType { CreateTunnelTerminatorRequestV2Type = 20413; CreateTunnelTerminatorResponseV2Type = 20414; + ConnectEventsTypes = 20415; DataStateType = 20500; DataStateChangeSetType = 20501; @@ -295,6 +296,7 @@ message ApiSession { string token = 1; repeated string certFingerprints = 2; string id = 3; + string identityId = 4; } message ApiSessionAdded { @@ -555,3 +557,19 @@ message EnrollmentCertsResponse { message EnrollmentExtendRouterVerifyRequest { string clientCertPem = 1; } + +message ConnectEvents { + message ConnectDetails { + int64 connectTime = 1; + string srcAddr = 2; + string dstAddr = 3; + } + message IdentityConnectEvents { + string identityId = 1; + bool isConnected = 2; + repeated ConnectDetails connectTimes = 3; + } + + repeated IdentityConnectEvents events = 1; + bool fullState = 2; +} \ No newline at end of file diff --git a/common/pb/edge_ctrl_pb/messages.go b/common/pb/edge_ctrl_pb/messages.go index 49ed705a2..ae27020a5 100644 --- a/common/pb/edge_ctrl_pb/messages.go +++ b/common/pb/edge_ctrl_pb/messages.go @@ -150,6 +150,10 @@ func (request *UpdateTunnelTerminatorRequest) GetContentType() int32 { return int32(ContentType_UpdateTunnelTerminatorRequestType) } +func (request *ConnectEvents) GetContentType() int32 { + return int32(ContentType_ConnectEventsTypes) +} + func GetPrecedence(p ziti.Precedence) TerminatorPrecedence { if p == ziti.PrecedenceRequired { return TerminatorPrecedence_Required diff --git a/common/pb/mgmt_pb/impl.go b/common/pb/mgmt_pb/impl.go index a81064118..b8506d876 100644 --- a/common/pb/mgmt_pb/impl.go +++ b/common/pb/mgmt_pb/impl.go @@ -59,3 +59,15 @@ func (request *ValidateRouterDataModelResponse) GetContentType() int32 { func (request *RouterDataModelDetails) GetContentType() int32 { return int32(ContentType_ValidateRouterDataModelResultType) } + +func (request *ValidateIdentityConnectionStatusesRequest) GetContentType() int32 { + return int32(ContentType_ValidateIdentityConnectionStatusesRequestType) +} + +func (request *ValidateIdentityConnectionStatusesResponse) GetContentType() int32 { + return int32(ContentType_ValidateIdentityConnectionStatusesResponseType) +} + +func (request *RouterIdentityConnectionStatusesDetails) GetContentType() int32 { + return int32(ContentType_ValidateIdentityConnectionStatusesResultType) +} diff --git a/common/pb/mgmt_pb/mgmt.pb.go b/common/pb/mgmt_pb/mgmt.pb.go index 946e7d43d..3d13ba444 100644 --- a/common/pb/mgmt_pb/mgmt.pb.go +++ b/common/pb/mgmt_pb/mgmt.pb.go @@ -56,18 +56,21 @@ const ( ContentType_RaftTransferLeadershipRequestType ContentType = 10084 ContentType_RaftInitFromDb ContentType = 10085 // Validate - ContentType_ValidateTerminatorsRequestType ContentType = 10100 - ContentType_ValidateTerminatorResponseType ContentType = 10101 - ContentType_ValidateTerminatorResultType ContentType = 10102 - ContentType_ValidateRouterLinksRequestType ContentType = 10103 - ContentType_ValidateRouterLinksResponseType ContentType = 10104 - ContentType_ValidateRouterLinksResultType ContentType = 10105 - ContentType_ValidateRouterSdkTerminatorsRequestType ContentType = 10106 - ContentType_ValidateRouterSdkTerminatorsResponseType ContentType = 10107 - ContentType_ValidateRouterSdkTerminatorsResultType ContentType = 10108 - ContentType_ValidateRouterDataModelRequestType ContentType = 10109 - ContentType_ValidateRouterDataModelResponseType ContentType = 10110 - ContentType_ValidateRouterDataModelResultType ContentType = 10111 + ContentType_ValidateTerminatorsRequestType ContentType = 10100 + ContentType_ValidateTerminatorResponseType ContentType = 10101 + ContentType_ValidateTerminatorResultType ContentType = 10102 + ContentType_ValidateRouterLinksRequestType ContentType = 10103 + ContentType_ValidateRouterLinksResponseType ContentType = 10104 + ContentType_ValidateRouterLinksResultType ContentType = 10105 + ContentType_ValidateRouterSdkTerminatorsRequestType ContentType = 10106 + ContentType_ValidateRouterSdkTerminatorsResponseType ContentType = 10107 + ContentType_ValidateRouterSdkTerminatorsResultType ContentType = 10108 + ContentType_ValidateRouterDataModelRequestType ContentType = 10109 + ContentType_ValidateRouterDataModelResponseType ContentType = 10110 + ContentType_ValidateRouterDataModelResultType ContentType = 10111 + ContentType_ValidateIdentityConnectionStatusesRequestType ContentType = 10112 + ContentType_ValidateIdentityConnectionStatusesResponseType ContentType = 10113 + ContentType_ValidateIdentityConnectionStatusesResultType ContentType = 10114 ) // Enum value maps for ContentType. @@ -110,45 +113,51 @@ var ( 10109: "ValidateRouterDataModelRequestType", 10110: "ValidateRouterDataModelResponseType", 10111: "ValidateRouterDataModelResultType", + 10112: "ValidateIdentityConnectionStatusesRequestType", + 10113: "ValidateIdentityConnectionStatusesResponseType", + 10114: "ValidateIdentityConnectionStatusesResultType", } ContentType_value = map[string]int32{ - "Zero": 0, - "StreamEventsRequestType": 10040, - "StreamEventsEventType": 10041, - "TogglePipeTracesRequestType": 10044, - "ToggleCircuitTracesRequestType": 10045, - "StreamTracesRequestType": 10046, - "StreamTracesEventType": 10047, - "InspectRequestType": 10048, - "InspectResponseType": 10049, - "SnapshotDbRequestType": 10070, - "RouterDebugForgetLinkRequestType": 10071, - "RouterDebugToggleCtrlChannelRequestType": 10072, - "RouterDebugUpdateRouteRequestType": 10073, - "RouterDebugDumpForwarderTablesRequestType": 10074, - "RouterDebugDumpLinksRequestType": 10075, - "RouterDebugUnrouteRequestType": 10076, - "RouterQuiesceRequestType": 10077, - "RouterDequiesceRequestType": 10078, - "RouterDecommissionRequestType": 10079, - "RaftListMembersRequestType": 10080, - "RaftListMembersResponseType": 10081, - "RaftAddPeerRequestType": 10082, - "RaftRemovePeerRequestType": 10083, - "RaftTransferLeadershipRequestType": 10084, - "RaftInitFromDb": 10085, - "ValidateTerminatorsRequestType": 10100, - "ValidateTerminatorResponseType": 10101, - "ValidateTerminatorResultType": 10102, - "ValidateRouterLinksRequestType": 10103, - "ValidateRouterLinksResponseType": 10104, - "ValidateRouterLinksResultType": 10105, - "ValidateRouterSdkTerminatorsRequestType": 10106, - "ValidateRouterSdkTerminatorsResponseType": 10107, - "ValidateRouterSdkTerminatorsResultType": 10108, - "ValidateRouterDataModelRequestType": 10109, - "ValidateRouterDataModelResponseType": 10110, - "ValidateRouterDataModelResultType": 10111, + "Zero": 0, + "StreamEventsRequestType": 10040, + "StreamEventsEventType": 10041, + "TogglePipeTracesRequestType": 10044, + "ToggleCircuitTracesRequestType": 10045, + "StreamTracesRequestType": 10046, + "StreamTracesEventType": 10047, + "InspectRequestType": 10048, + "InspectResponseType": 10049, + "SnapshotDbRequestType": 10070, + "RouterDebugForgetLinkRequestType": 10071, + "RouterDebugToggleCtrlChannelRequestType": 10072, + "RouterDebugUpdateRouteRequestType": 10073, + "RouterDebugDumpForwarderTablesRequestType": 10074, + "RouterDebugDumpLinksRequestType": 10075, + "RouterDebugUnrouteRequestType": 10076, + "RouterQuiesceRequestType": 10077, + "RouterDequiesceRequestType": 10078, + "RouterDecommissionRequestType": 10079, + "RaftListMembersRequestType": 10080, + "RaftListMembersResponseType": 10081, + "RaftAddPeerRequestType": 10082, + "RaftRemovePeerRequestType": 10083, + "RaftTransferLeadershipRequestType": 10084, + "RaftInitFromDb": 10085, + "ValidateTerminatorsRequestType": 10100, + "ValidateTerminatorResponseType": 10101, + "ValidateTerminatorResultType": 10102, + "ValidateRouterLinksRequestType": 10103, + "ValidateRouterLinksResponseType": 10104, + "ValidateRouterLinksResultType": 10105, + "ValidateRouterSdkTerminatorsRequestType": 10106, + "ValidateRouterSdkTerminatorsResponseType": 10107, + "ValidateRouterSdkTerminatorsResultType": 10108, + "ValidateRouterDataModelRequestType": 10109, + "ValidateRouterDataModelResponseType": 10110, + "ValidateRouterDataModelResultType": 10111, + "ValidateIdentityConnectionStatusesRequestType": 10112, + "ValidateIdentityConnectionStatusesResponseType": 10113, + "ValidateIdentityConnectionStatusesResultType": 10114, } ) @@ -2136,6 +2145,195 @@ func (x *RouterDataModelDetails) GetErrors() []string { return nil } +type ValidateIdentityConnectionStatusesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RouterFilter string `protobuf:"bytes,1,opt,name=routerFilter,proto3" json:"routerFilter,omitempty"` +} + +func (x *ValidateIdentityConnectionStatusesRequest) Reset() { + *x = ValidateIdentityConnectionStatusesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_mgmt_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateIdentityConnectionStatusesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateIdentityConnectionStatusesRequest) ProtoMessage() {} + +func (x *ValidateIdentityConnectionStatusesRequest) ProtoReflect() protoreflect.Message { + mi := &file_mgmt_proto_msgTypes[24] + 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 ValidateIdentityConnectionStatusesRequest.ProtoReflect.Descriptor instead. +func (*ValidateIdentityConnectionStatusesRequest) Descriptor() ([]byte, []int) { + return file_mgmt_proto_rawDescGZIP(), []int{24} +} + +func (x *ValidateIdentityConnectionStatusesRequest) GetRouterFilter() string { + if x != nil { + return x.RouterFilter + } + return "" +} + +type ValidateIdentityConnectionStatusesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"` + Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` + ComponentCount uint64 `protobuf:"varint,3,opt,name=componentCount,proto3" json:"componentCount,omitempty"` +} + +func (x *ValidateIdentityConnectionStatusesResponse) Reset() { + *x = ValidateIdentityConnectionStatusesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_mgmt_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ValidateIdentityConnectionStatusesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ValidateIdentityConnectionStatusesResponse) ProtoMessage() {} + +func (x *ValidateIdentityConnectionStatusesResponse) ProtoReflect() protoreflect.Message { + mi := &file_mgmt_proto_msgTypes[25] + 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 ValidateIdentityConnectionStatusesResponse.ProtoReflect.Descriptor instead. +func (*ValidateIdentityConnectionStatusesResponse) Descriptor() ([]byte, []int) { + return file_mgmt_proto_rawDescGZIP(), []int{25} +} + +func (x *ValidateIdentityConnectionStatusesResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *ValidateIdentityConnectionStatusesResponse) GetMessage() string { + if x != nil { + return x.Message + } + return "" +} + +func (x *ValidateIdentityConnectionStatusesResponse) GetComponentCount() uint64 { + if x != nil { + return x.ComponentCount + } + return 0 +} + +type RouterIdentityConnectionStatusesDetails struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ComponentType string `protobuf:"bytes,1,opt,name=componentType,proto3" json:"componentType,omitempty"` + ComponentId string `protobuf:"bytes,2,opt,name=componentId,proto3" json:"componentId,omitempty"` + ComponentName string `protobuf:"bytes,3,opt,name=componentName,proto3" json:"componentName,omitempty"` + ValidateSuccess bool `protobuf:"varint,4,opt,name=validateSuccess,proto3" json:"validateSuccess,omitempty"` + Errors []string `protobuf:"bytes,5,rep,name=errors,proto3" json:"errors,omitempty"` +} + +func (x *RouterIdentityConnectionStatusesDetails) Reset() { + *x = RouterIdentityConnectionStatusesDetails{} + if protoimpl.UnsafeEnabled { + mi := &file_mgmt_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RouterIdentityConnectionStatusesDetails) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RouterIdentityConnectionStatusesDetails) ProtoMessage() {} + +func (x *RouterIdentityConnectionStatusesDetails) ProtoReflect() protoreflect.Message { + mi := &file_mgmt_proto_msgTypes[26] + 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 RouterIdentityConnectionStatusesDetails.ProtoReflect.Descriptor instead. +func (*RouterIdentityConnectionStatusesDetails) Descriptor() ([]byte, []int) { + return file_mgmt_proto_rawDescGZIP(), []int{26} +} + +func (x *RouterIdentityConnectionStatusesDetails) GetComponentType() string { + if x != nil { + return x.ComponentType + } + return "" +} + +func (x *RouterIdentityConnectionStatusesDetails) GetComponentId() string { + if x != nil { + return x.ComponentId + } + return "" +} + +func (x *RouterIdentityConnectionStatusesDetails) GetComponentName() string { + if x != nil { + return x.ComponentName + } + return "" +} + +func (x *RouterIdentityConnectionStatusesDetails) GetValidateSuccess() bool { + if x != nil { + return x.ValidateSuccess + } + return false +} + +func (x *RouterIdentityConnectionStatusesDetails) GetErrors() []string { + if x != nil { + return x.Errors + } + return nil +} + type StreamMetricsRequest_MetricMatcher struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2148,7 +2346,7 @@ type StreamMetricsRequest_MetricMatcher struct { func (x *StreamMetricsRequest_MetricMatcher) Reset() { *x = StreamMetricsRequest_MetricMatcher{} if protoimpl.UnsafeEnabled { - mi := &file_mgmt_proto_msgTypes[24] + mi := &file_mgmt_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2161,7 +2359,7 @@ func (x *StreamMetricsRequest_MetricMatcher) String() string { func (*StreamMetricsRequest_MetricMatcher) ProtoMessage() {} func (x *StreamMetricsRequest_MetricMatcher) ProtoReflect() protoreflect.Message { - mi := &file_mgmt_proto_msgTypes[24] + mi := &file_mgmt_proto_msgTypes[27] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2205,7 +2403,7 @@ type StreamMetricsEvent_IntervalMetric struct { func (x *StreamMetricsEvent_IntervalMetric) Reset() { *x = StreamMetricsEvent_IntervalMetric{} if protoimpl.UnsafeEnabled { - mi := &file_mgmt_proto_msgTypes[28] + mi := &file_mgmt_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2218,7 +2416,7 @@ func (x *StreamMetricsEvent_IntervalMetric) String() string { func (*StreamMetricsEvent_IntervalMetric) ProtoMessage() {} func (x *StreamMetricsEvent_IntervalMetric) ProtoReflect() protoreflect.Message { - mi := &file_mgmt_proto_msgTypes[28] + mi := &file_mgmt_proto_msgTypes[31] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2275,7 +2473,7 @@ type InspectResponse_InspectValue struct { func (x *InspectResponse_InspectValue) Reset() { *x = InspectResponse_InspectValue{} if protoimpl.UnsafeEnabled { - mi := &file_mgmt_proto_msgTypes[31] + mi := &file_mgmt_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2288,7 +2486,7 @@ func (x *InspectResponse_InspectValue) String() string { func (*InspectResponse_InspectValue) ProtoMessage() {} func (x *InspectResponse_InspectValue) ProtoReflect() protoreflect.Message { - mi := &file_mgmt_proto_msgTypes[31] + mi := &file_mgmt_proto_msgTypes[34] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2641,121 +2839,158 @@ var file_mgmt_proto_rawDesc = []byte{ 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, - 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2a, 0x9a, - 0x0a, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, - 0x0a, 0x04, 0x5a, 0x65, 0x72, 0x6f, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x10, 0xb8, 0x4e, 0x12, 0x1a, 0x0a, 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, - 0xb9, 0x4e, 0x12, 0x20, 0x0a, 0x1b, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x50, 0x69, 0x70, 0x65, - 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x10, 0xbc, 0x4e, 0x12, 0x23, 0x0a, 0x1e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x69, - 0x72, 0x63, 0x75, 0x69, 0x74, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbd, 0x4e, 0x12, 0x1c, 0x0a, 0x17, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x10, 0xbe, 0x4e, 0x12, 0x1a, 0x0a, 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x10, 0xbf, 0x4e, 0x12, 0x17, 0x0a, 0x12, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xc0, 0x4e, 0x12, 0x18, 0x0a, 0x13, - 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, - 0x79, 0x70, 0x65, 0x10, 0xc1, 0x4e, 0x12, 0x1a, 0x0a, 0x15, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, - 0x6f, 0x74, 0x44, 0x62, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, - 0xd6, 0x4e, 0x12, 0x25, 0x0a, 0x20, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, - 0x67, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x4c, 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xd7, 0x4e, 0x12, 0x2c, 0x0a, 0x27, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x74, - 0x72, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x10, 0xd8, 0x4e, 0x12, 0x26, 0x0a, 0x21, 0x52, 0x6f, 0x75, 0x74, 0x65, - 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xd9, 0x4e, 0x12, - 0x2e, 0x0a, 0x29, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x75, - 0x6d, 0x70, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, 0x64, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xda, 0x4e, 0x12, - 0x24, 0x0a, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x75, - 0x6d, 0x70, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x10, 0xdb, 0x4e, 0x12, 0x22, 0x0a, 0x1d, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, - 0x65, 0x62, 0x75, 0x67, 0x55, 0x6e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xdc, 0x4e, 0x12, 0x1d, 0x0a, 0x18, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x51, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xdd, 0x4e, 0x12, 0x1f, 0x0a, 0x1a, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x44, 0x65, 0x71, 0x75, 0x69, 0x65, 0x73, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xde, 0x4e, 0x12, 0x22, 0x0a, 0x1d, 0x52, 0x6f, 0x75, - 0x74, 0x65, 0x72, 0x44, 0x65, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xdf, 0x4e, 0x12, 0x1f, 0x0a, - 0x1a, 0x52, 0x61, 0x66, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe0, 0x4e, 0x12, 0x20, - 0x0a, 0x1b, 0x52, 0x61, 0x66, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe1, 0x4e, - 0x12, 0x1b, 0x0a, 0x16, 0x52, 0x61, 0x66, 0x74, 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe2, 0x4e, 0x12, 0x1e, 0x0a, - 0x19, 0x52, 0x61, 0x66, 0x74, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x50, 0x65, 0x65, 0x72, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe3, 0x4e, 0x12, 0x26, 0x0a, - 0x21, 0x52, 0x61, 0x66, 0x74, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x10, 0xe4, 0x4e, 0x12, 0x13, 0x0a, 0x0e, 0x52, 0x61, 0x66, 0x74, 0x49, 0x6e, 0x69, - 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x44, 0x62, 0x10, 0xe5, 0x4e, 0x12, 0x23, 0x0a, 0x1e, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, - 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf4, 0x4e, 0x12, - 0x23, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, - 0x65, 0x10, 0xf5, 0x4e, 0x12, 0x21, 0x0a, 0x1c, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x10, 0xf6, 0x4e, 0x12, 0x23, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, - 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf7, 0x4e, 0x12, 0x24, 0x0a, 0x1f, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, - 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, - 0xf8, 0x4e, 0x12, 0x22, 0x0a, 0x1d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, - 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x10, 0xf9, 0x4e, 0x12, 0x2c, 0x0a, 0x27, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x64, 0x6b, 0x54, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x10, 0xfa, 0x4e, 0x12, 0x2d, 0x0a, 0x28, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x64, 0x6b, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, - 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x10, 0xfb, 0x4e, 0x12, 0x2b, 0x0a, 0x26, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x64, 0x6b, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, - 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfc, 0x4e, - 0x12, 0x27, 0x0a, 0x22, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, - 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfd, 0x4e, 0x12, 0x28, 0x0a, 0x23, 0x56, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, - 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, - 0x10, 0xfe, 0x4e, 0x12, 0x26, 0x0a, 0x21, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, - 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, - 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xff, 0x4e, 0x2a, 0x53, 0x0a, 0x06, 0x48, - 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x6f, 0x6e, 0x65, 0x48, 0x65, 0x61, - 0x64, 0x65, 0x72, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, - 0x70, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x10, 0x0a, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x74, - 0x72, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x10, 0x0b, 0x12, 0x10, - 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x49, 0x64, 0x10, 0x0c, - 0x2a, 0x78, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, - 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x69, - 0x72, 0x63, 0x75, 0x69, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x10, 0x00, 0x12, 0x12, - 0x0a, 0x0e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, - 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x50, 0x72, 0x65, - 0x73, 0x65, 0x6e, 0x74, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x64, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x43, 0x69, 0x72, 0x63, 0x75, - 0x69, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x04, 0x2a, 0x2b, 0x0a, 0x0f, 0x54, 0x72, - 0x61, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, - 0x07, 0x45, 0x58, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, - 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x01, 0x2a, 0x77, 0x0a, 0x0f, 0x54, 0x65, 0x72, 0x6d, 0x69, - 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09, 0x0a, 0x05, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, - 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x6b, - 0x6e, 0x6f, 0x77, 0x6e, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x12, 0x1c, 0x0a, - 0x18, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x54, - 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x49, - 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x61, 0x64, 0x53, 0x74, 0x61, 0x74, 0x65, 0x10, 0x04, - 0x2a, 0x53, 0x0a, 0x09, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x0f, 0x0a, - 0x0b, 0x4c, 0x69, 0x6e, 0x6b, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x13, - 0x0a, 0x0f, 0x4c, 0x69, 0x6e, 0x6b, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x65, - 0x64, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x6b, 0x50, 0x65, 0x6e, 0x64, 0x69, - 0x6e, 0x67, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x6b, 0x44, 0x69, 0x61, 0x6c, - 0x69, 0x6e, 0x67, 0x10, 0x03, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x7a, 0x69, 0x74, 0x69, 0x2f, 0x66, 0x61, 0x62, - 0x72, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x67, 0x6d, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x4f, + 0x0a, 0x29, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x72, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0c, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, + 0x88, 0x01, 0x0a, 0x2a, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, + 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x43, + 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x63, 0x6f, 0x6d, 0x70, + 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0xd9, 0x01, 0x0a, 0x27, 0x52, + 0x6f, 0x75, 0x74, 0x65, 0x72, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, + 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x44, + 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, + 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x20, 0x0a, 0x0b, + 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x24, + 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, + 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x16, + 0x0a, 0x06, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, + 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2a, 0xb6, 0x0b, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x5a, 0x65, 0x72, 0x6f, 0x10, 0x00, + 0x12, 0x1c, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb8, 0x4e, 0x12, 0x1a, + 0x0a, 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xb9, 0x4e, 0x12, 0x20, 0x0a, 0x1b, 0x54, 0x6f, + 0x67, 0x67, 0x6c, 0x65, 0x50, 0x69, 0x70, 0x65, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbc, 0x4e, 0x12, 0x23, 0x0a, 0x1e, + 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x54, 0x72, 0x61, + 0x63, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbd, + 0x4e, 0x12, 0x1c, 0x0a, 0x17, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x72, 0x61, 0x63, 0x65, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbe, 0x4e, 0x12, + 0x1a, 0x0a, 0x15, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x54, 0x72, 0x61, 0x63, 0x65, 0x73, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xbf, 0x4e, 0x12, 0x17, 0x0a, 0x12, 0x49, + 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x10, 0xc0, 0x4e, 0x12, 0x18, 0x0a, 0x13, 0x49, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xc1, 0x4e, 0x12, 0x1a, + 0x0a, 0x15, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x44, 0x62, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xd6, 0x4e, 0x12, 0x25, 0x0a, 0x20, 0x52, 0x6f, + 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x46, 0x6f, 0x72, 0x67, 0x65, 0x74, 0x4c, + 0x69, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xd7, + 0x4e, 0x12, 0x2c, 0x0a, 0x27, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, + 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, 0x43, 0x74, 0x72, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, + 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xd8, 0x4e, 0x12, + 0x26, 0x0a, 0x21, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x10, 0xd9, 0x4e, 0x12, 0x2e, 0x0a, 0x29, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x75, 0x6d, 0x70, 0x46, 0x6f, 0x72, 0x77, 0x61, 0x72, + 0x64, 0x65, 0x72, 0x54, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x10, 0xda, 0x4e, 0x12, 0x24, 0x0a, 0x1f, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x44, 0x75, 0x6d, 0x70, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xdb, 0x4e, 0x12, 0x22, 0x0a, + 0x1d, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x62, 0x75, 0x67, 0x55, 0x6e, 0x72, 0x6f, + 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xdc, + 0x4e, 0x12, 0x1d, 0x0a, 0x18, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x51, 0x75, 0x69, 0x65, 0x73, + 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xdd, 0x4e, + 0x12, 0x1f, 0x0a, 0x1a, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x71, 0x75, 0x69, 0x65, + 0x73, 0x63, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xde, + 0x4e, 0x12, 0x22, 0x0a, 0x1d, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x65, 0x63, 0x6f, 0x6d, + 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x10, 0xdf, 0x4e, 0x12, 0x1f, 0x0a, 0x1a, 0x52, 0x61, 0x66, 0x74, 0x4c, 0x69, 0x73, + 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x10, 0xe0, 0x4e, 0x12, 0x20, 0x0a, 0x1b, 0x52, 0x61, 0x66, 0x74, 0x4c, 0x69, + 0x73, 0x74, 0x4d, 0x65, 0x6d, 0x62, 0x65, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe1, 0x4e, 0x12, 0x1b, 0x0a, 0x16, 0x52, 0x61, 0x66, 0x74, + 0x41, 0x64, 0x64, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x10, 0xe2, 0x4e, 0x12, 0x1e, 0x0a, 0x19, 0x52, 0x61, 0x66, 0x74, 0x52, 0x65, 0x6d, + 0x6f, 0x76, 0x65, 0x50, 0x65, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, + 0x70, 0x65, 0x10, 0xe3, 0x4e, 0x12, 0x26, 0x0a, 0x21, 0x52, 0x61, 0x66, 0x74, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x66, 0x65, 0x72, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x68, 0x69, 0x70, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xe4, 0x4e, 0x12, 0x13, 0x0a, + 0x0e, 0x52, 0x61, 0x66, 0x74, 0x49, 0x6e, 0x69, 0x74, 0x46, 0x72, 0x6f, 0x6d, 0x44, 0x62, 0x10, + 0xe5, 0x4e, 0x12, 0x23, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, + 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x10, 0xf4, 0x4e, 0x12, 0x23, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf5, 0x4e, 0x12, 0x21, 0x0a, 0x1c, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, + 0x6f, 0x72, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf6, 0x4e, 0x12, + 0x23, 0x0a, 0x1e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, + 0x72, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x10, 0xf7, 0x4e, 0x12, 0x24, 0x0a, 0x1f, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf8, 0x4e, 0x12, 0x22, 0x0a, 0x1d, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x4c, 0x69, 0x6e, 0x6b, + 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xf9, 0x4e, 0x12, 0x2c, + 0x0a, 0x27, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, + 0x53, 0x64, 0x6b, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfa, 0x4e, 0x12, 0x2d, 0x0a, 0x28, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x64, + 0x6b, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfb, 0x4e, 0x12, 0x2b, 0x0a, 0x26, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x53, 0x64, 0x6b, + 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, + 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfc, 0x4e, 0x12, 0x27, 0x0a, 0x22, 0x56, 0x61, 0x6c, 0x69, + 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x6f, + 0x64, 0x65, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfd, + 0x4e, 0x12, 0x28, 0x0a, 0x23, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, + 0x74, 0x65, 0x72, 0x44, 0x61, 0x74, 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0xfe, 0x4e, 0x12, 0x26, 0x0a, 0x21, 0x56, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x44, 0x61, 0x74, + 0x61, 0x4d, 0x6f, 0x64, 0x65, 0x6c, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x10, 0xff, 0x4e, 0x12, 0x32, 0x0a, 0x2d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, + 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x10, 0x80, 0x4f, 0x12, 0x33, 0x0a, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, + 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x6e, 0x65, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x10, 0x81, 0x4f, 0x12, 0x31, 0x0a, 0x2c, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x65, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x54, 0x79, 0x70, 0x65, 0x10, 0x82, 0x4f, 0x2a, + 0x53, 0x0a, 0x06, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x0e, 0x0a, 0x0a, 0x4e, 0x6f, 0x6e, + 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x10, 0x0a, 0x12, 0x12, + 0x0a, 0x0e, 0x43, 0x74, 0x72, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x54, 0x6f, 0x67, 0x67, 0x6c, 0x65, + 0x10, 0x0b, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x49, 0x64, 0x10, 0x0c, 0x2a, 0x78, 0x0a, 0x16, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x69, + 0x72, 0x63, 0x75, 0x69, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, + 0x0a, 0x0e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x10, 0x00, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x64, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x43, 0x69, 0x72, 0x63, 0x75, 0x69, + 0x74, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x50, 0x61, + 0x74, 0x68, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x10, 0x03, 0x12, 0x11, 0x0a, 0x0d, 0x43, + 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x10, 0x04, 0x2a, 0x2b, + 0x0a, 0x0f, 0x54, 0x72, 0x61, 0x63, 0x65, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x54, 0x79, 0x70, + 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x45, 0x58, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x00, 0x12, 0x0b, + 0x0a, 0x07, 0x49, 0x4e, 0x43, 0x4c, 0x55, 0x44, 0x45, 0x10, 0x01, 0x2a, 0x77, 0x0a, 0x0f, 0x54, + 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x09, + 0x0a, 0x05, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, + 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x01, 0x12, 0x19, 0x0a, 0x15, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, + 0x64, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x42, 0x69, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x10, + 0x02, 0x12, 0x1c, 0x0a, 0x18, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x55, 0x6e, 0x6b, 0x6e, + 0x6f, 0x77, 0x6e, 0x54, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x74, 0x6f, 0x72, 0x10, 0x03, 0x12, + 0x13, 0x0a, 0x0f, 0x49, 0x6e, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x42, 0x61, 0x64, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x10, 0x04, 0x2a, 0x53, 0x0a, 0x09, 0x4c, 0x69, 0x6e, 0x6b, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x6b, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, + 0x10, 0x00, 0x12, 0x13, 0x0a, 0x0f, 0x4c, 0x69, 0x6e, 0x6b, 0x45, 0x73, 0x74, 0x61, 0x62, 0x6c, + 0x69, 0x73, 0x68, 0x65, 0x64, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x6b, 0x50, + 0x65, 0x6e, 0x64, 0x69, 0x6e, 0x67, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x4c, 0x69, 0x6e, 0x6b, + 0x44, 0x69, 0x61, 0x6c, 0x69, 0x6e, 0x67, 0x10, 0x03, 0x42, 0x27, 0x5a, 0x25, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x7a, 0x69, 0x74, 0x69, + 0x2f, 0x66, 0x61, 0x62, 0x72, 0x69, 0x63, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x67, 0x6d, 0x74, 0x5f, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -2771,60 +3006,63 @@ func file_mgmt_proto_rawDescGZIP() []byte { } var file_mgmt_proto_enumTypes = make([]protoimpl.EnumInfo, 6) -var file_mgmt_proto_msgTypes = make([]protoimpl.MessageInfo, 32) +var file_mgmt_proto_msgTypes = make([]protoimpl.MessageInfo, 35) var file_mgmt_proto_goTypes = []interface{}{ - (ContentType)(0), // 0: ziti.mgmt_pb.ContentType - (Header)(0), // 1: ziti.mgmt_pb.Header - (StreamCircuitEventType)(0), // 2: ziti.mgmt_pb.StreamCircuitEventType - (TraceFilterType)(0), // 3: ziti.mgmt_pb.TraceFilterType - (TerminatorState)(0), // 4: ziti.mgmt_pb.TerminatorState - (LinkState)(0), // 5: ziti.mgmt_pb.LinkState - (*StreamMetricsRequest)(nil), // 6: ziti.mgmt_pb.StreamMetricsRequest - (*StreamMetricsEvent)(nil), // 7: ziti.mgmt_pb.StreamMetricsEvent - (*Path)(nil), // 8: ziti.mgmt_pb.Path - (*StreamCircuitsEvent)(nil), // 9: ziti.mgmt_pb.StreamCircuitsEvent - (*ToggleCircuitTracesRequest)(nil), // 10: ziti.mgmt_pb.ToggleCircuitTracesRequest - (*StreamTracesRequest)(nil), // 11: ziti.mgmt_pb.StreamTracesRequest - (*InspectRequest)(nil), // 12: ziti.mgmt_pb.InspectRequest - (*InspectResponse)(nil), // 13: ziti.mgmt_pb.InspectResponse - (*RaftMember)(nil), // 14: ziti.mgmt_pb.RaftMember - (*RaftMemberListResponse)(nil), // 15: ziti.mgmt_pb.RaftMemberListResponse - (*ValidateTerminatorsRequest)(nil), // 16: ziti.mgmt_pb.ValidateTerminatorsRequest - (*ValidateTerminatorsResponse)(nil), // 17: ziti.mgmt_pb.ValidateTerminatorsResponse - (*TerminatorDetail)(nil), // 18: ziti.mgmt_pb.TerminatorDetail - (*ValidateRouterLinksRequest)(nil), // 19: ziti.mgmt_pb.ValidateRouterLinksRequest - (*ValidateRouterLinksResponse)(nil), // 20: ziti.mgmt_pb.ValidateRouterLinksResponse - (*RouterLinkDetails)(nil), // 21: ziti.mgmt_pb.RouterLinkDetails - (*RouterLinkDetail)(nil), // 22: ziti.mgmt_pb.RouterLinkDetail - (*ValidateRouterSdkTerminatorsRequest)(nil), // 23: ziti.mgmt_pb.ValidateRouterSdkTerminatorsRequest - (*ValidateRouterSdkTerminatorsResponse)(nil), // 24: ziti.mgmt_pb.ValidateRouterSdkTerminatorsResponse - (*RouterSdkTerminatorsDetails)(nil), // 25: ziti.mgmt_pb.RouterSdkTerminatorsDetails - (*RouterSdkTerminatorDetail)(nil), // 26: ziti.mgmt_pb.RouterSdkTerminatorDetail - (*ValidateRouterDataModelRequest)(nil), // 27: ziti.mgmt_pb.ValidateRouterDataModelRequest - (*ValidateRouterDataModelResponse)(nil), // 28: ziti.mgmt_pb.ValidateRouterDataModelResponse - (*RouterDataModelDetails)(nil), // 29: ziti.mgmt_pb.RouterDataModelDetails - (*StreamMetricsRequest_MetricMatcher)(nil), // 30: ziti.mgmt_pb.StreamMetricsRequest.MetricMatcher - nil, // 31: ziti.mgmt_pb.StreamMetricsEvent.TagsEntry - nil, // 32: ziti.mgmt_pb.StreamMetricsEvent.IntMetricsEntry - nil, // 33: ziti.mgmt_pb.StreamMetricsEvent.FloatMetricsEntry - (*StreamMetricsEvent_IntervalMetric)(nil), // 34: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric - nil, // 35: ziti.mgmt_pb.StreamMetricsEvent.MetricGroupEntry - nil, // 36: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.ValuesEntry - (*InspectResponse_InspectValue)(nil), // 37: ziti.mgmt_pb.InspectResponse.InspectValue - (*timestamppb.Timestamp)(nil), // 38: google.protobuf.Timestamp + (ContentType)(0), // 0: ziti.mgmt_pb.ContentType + (Header)(0), // 1: ziti.mgmt_pb.Header + (StreamCircuitEventType)(0), // 2: ziti.mgmt_pb.StreamCircuitEventType + (TraceFilterType)(0), // 3: ziti.mgmt_pb.TraceFilterType + (TerminatorState)(0), // 4: ziti.mgmt_pb.TerminatorState + (LinkState)(0), // 5: ziti.mgmt_pb.LinkState + (*StreamMetricsRequest)(nil), // 6: ziti.mgmt_pb.StreamMetricsRequest + (*StreamMetricsEvent)(nil), // 7: ziti.mgmt_pb.StreamMetricsEvent + (*Path)(nil), // 8: ziti.mgmt_pb.Path + (*StreamCircuitsEvent)(nil), // 9: ziti.mgmt_pb.StreamCircuitsEvent + (*ToggleCircuitTracesRequest)(nil), // 10: ziti.mgmt_pb.ToggleCircuitTracesRequest + (*StreamTracesRequest)(nil), // 11: ziti.mgmt_pb.StreamTracesRequest + (*InspectRequest)(nil), // 12: ziti.mgmt_pb.InspectRequest + (*InspectResponse)(nil), // 13: ziti.mgmt_pb.InspectResponse + (*RaftMember)(nil), // 14: ziti.mgmt_pb.RaftMember + (*RaftMemberListResponse)(nil), // 15: ziti.mgmt_pb.RaftMemberListResponse + (*ValidateTerminatorsRequest)(nil), // 16: ziti.mgmt_pb.ValidateTerminatorsRequest + (*ValidateTerminatorsResponse)(nil), // 17: ziti.mgmt_pb.ValidateTerminatorsResponse + (*TerminatorDetail)(nil), // 18: ziti.mgmt_pb.TerminatorDetail + (*ValidateRouterLinksRequest)(nil), // 19: ziti.mgmt_pb.ValidateRouterLinksRequest + (*ValidateRouterLinksResponse)(nil), // 20: ziti.mgmt_pb.ValidateRouterLinksResponse + (*RouterLinkDetails)(nil), // 21: ziti.mgmt_pb.RouterLinkDetails + (*RouterLinkDetail)(nil), // 22: ziti.mgmt_pb.RouterLinkDetail + (*ValidateRouterSdkTerminatorsRequest)(nil), // 23: ziti.mgmt_pb.ValidateRouterSdkTerminatorsRequest + (*ValidateRouterSdkTerminatorsResponse)(nil), // 24: ziti.mgmt_pb.ValidateRouterSdkTerminatorsResponse + (*RouterSdkTerminatorsDetails)(nil), // 25: ziti.mgmt_pb.RouterSdkTerminatorsDetails + (*RouterSdkTerminatorDetail)(nil), // 26: ziti.mgmt_pb.RouterSdkTerminatorDetail + (*ValidateRouterDataModelRequest)(nil), // 27: ziti.mgmt_pb.ValidateRouterDataModelRequest + (*ValidateRouterDataModelResponse)(nil), // 28: ziti.mgmt_pb.ValidateRouterDataModelResponse + (*RouterDataModelDetails)(nil), // 29: ziti.mgmt_pb.RouterDataModelDetails + (*ValidateIdentityConnectionStatusesRequest)(nil), // 30: ziti.mgmt_pb.ValidateIdentityConnectionStatusesRequest + (*ValidateIdentityConnectionStatusesResponse)(nil), // 31: ziti.mgmt_pb.ValidateIdentityConnectionStatusesResponse + (*RouterIdentityConnectionStatusesDetails)(nil), // 32: ziti.mgmt_pb.RouterIdentityConnectionStatusesDetails + (*StreamMetricsRequest_MetricMatcher)(nil), // 33: ziti.mgmt_pb.StreamMetricsRequest.MetricMatcher + nil, // 34: ziti.mgmt_pb.StreamMetricsEvent.TagsEntry + nil, // 35: ziti.mgmt_pb.StreamMetricsEvent.IntMetricsEntry + nil, // 36: ziti.mgmt_pb.StreamMetricsEvent.FloatMetricsEntry + (*StreamMetricsEvent_IntervalMetric)(nil), // 37: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric + nil, // 38: ziti.mgmt_pb.StreamMetricsEvent.MetricGroupEntry + nil, // 39: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.ValuesEntry + (*InspectResponse_InspectValue)(nil), // 40: ziti.mgmt_pb.InspectResponse.InspectValue + (*timestamppb.Timestamp)(nil), // 41: google.protobuf.Timestamp } var file_mgmt_proto_depIdxs = []int32{ - 30, // 0: ziti.mgmt_pb.StreamMetricsRequest.matchers:type_name -> ziti.mgmt_pb.StreamMetricsRequest.MetricMatcher - 38, // 1: ziti.mgmt_pb.StreamMetricsEvent.timestamp:type_name -> google.protobuf.Timestamp - 31, // 2: ziti.mgmt_pb.StreamMetricsEvent.tags:type_name -> ziti.mgmt_pb.StreamMetricsEvent.TagsEntry - 32, // 3: ziti.mgmt_pb.StreamMetricsEvent.intMetrics:type_name -> ziti.mgmt_pb.StreamMetricsEvent.IntMetricsEntry - 33, // 4: ziti.mgmt_pb.StreamMetricsEvent.floatMetrics:type_name -> ziti.mgmt_pb.StreamMetricsEvent.FloatMetricsEntry - 34, // 5: ziti.mgmt_pb.StreamMetricsEvent.intervalMetrics:type_name -> ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric - 35, // 6: ziti.mgmt_pb.StreamMetricsEvent.metricGroup:type_name -> ziti.mgmt_pb.StreamMetricsEvent.MetricGroupEntry + 33, // 0: ziti.mgmt_pb.StreamMetricsRequest.matchers:type_name -> ziti.mgmt_pb.StreamMetricsRequest.MetricMatcher + 41, // 1: ziti.mgmt_pb.StreamMetricsEvent.timestamp:type_name -> google.protobuf.Timestamp + 34, // 2: ziti.mgmt_pb.StreamMetricsEvent.tags:type_name -> ziti.mgmt_pb.StreamMetricsEvent.TagsEntry + 35, // 3: ziti.mgmt_pb.StreamMetricsEvent.intMetrics:type_name -> ziti.mgmt_pb.StreamMetricsEvent.IntMetricsEntry + 36, // 4: ziti.mgmt_pb.StreamMetricsEvent.floatMetrics:type_name -> ziti.mgmt_pb.StreamMetricsEvent.FloatMetricsEntry + 37, // 5: ziti.mgmt_pb.StreamMetricsEvent.intervalMetrics:type_name -> ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric + 38, // 6: ziti.mgmt_pb.StreamMetricsEvent.metricGroup:type_name -> ziti.mgmt_pb.StreamMetricsEvent.MetricGroupEntry 2, // 7: ziti.mgmt_pb.StreamCircuitsEvent.eventType:type_name -> ziti.mgmt_pb.StreamCircuitEventType 8, // 8: ziti.mgmt_pb.StreamCircuitsEvent.path:type_name -> ziti.mgmt_pb.Path 3, // 9: ziti.mgmt_pb.StreamTracesRequest.filterType:type_name -> ziti.mgmt_pb.TraceFilterType - 37, // 10: ziti.mgmt_pb.InspectResponse.values:type_name -> ziti.mgmt_pb.InspectResponse.InspectValue + 40, // 10: ziti.mgmt_pb.InspectResponse.values:type_name -> ziti.mgmt_pb.InspectResponse.InspectValue 14, // 11: ziti.mgmt_pb.RaftMemberListResponse.members:type_name -> ziti.mgmt_pb.RaftMember 4, // 12: ziti.mgmt_pb.TerminatorDetail.state:type_name -> ziti.mgmt_pb.TerminatorState 22, // 13: ziti.mgmt_pb.RouterLinkDetails.linkDetails:type_name -> ziti.mgmt_pb.RouterLinkDetail @@ -2832,9 +3070,9 @@ var file_mgmt_proto_depIdxs = []int32{ 5, // 15: ziti.mgmt_pb.RouterLinkDetail.routerState:type_name -> ziti.mgmt_pb.LinkState 26, // 16: ziti.mgmt_pb.RouterSdkTerminatorsDetails.details:type_name -> ziti.mgmt_pb.RouterSdkTerminatorDetail 4, // 17: ziti.mgmt_pb.RouterSdkTerminatorDetail.ctrlState:type_name -> ziti.mgmt_pb.TerminatorState - 38, // 18: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.intervalStartUTC:type_name -> google.protobuf.Timestamp - 38, // 19: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.intervalEndUTC:type_name -> google.protobuf.Timestamp - 36, // 20: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.values:type_name -> ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.ValuesEntry + 41, // 18: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.intervalStartUTC:type_name -> google.protobuf.Timestamp + 41, // 19: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.intervalEndUTC:type_name -> google.protobuf.Timestamp + 39, // 20: ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.values:type_name -> ziti.mgmt_pb.StreamMetricsEvent.IntervalMetric.ValuesEntry 21, // [21:21] is the sub-list for method output_type 21, // [21:21] is the sub-list for method input_type 21, // [21:21] is the sub-list for extension type_name @@ -3137,6 +3375,42 @@ func file_mgmt_proto_init() { } } file_mgmt_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateIdentityConnectionStatusesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mgmt_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ValidateIdentityConnectionStatusesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mgmt_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RouterIdentityConnectionStatusesDetails); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_mgmt_proto_msgTypes[27].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StreamMetricsRequest_MetricMatcher); i { case 0: return &v.state @@ -3148,7 +3422,7 @@ func file_mgmt_proto_init() { return nil } } - file_mgmt_proto_msgTypes[28].Exporter = func(v interface{}, i int) interface{} { + file_mgmt_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*StreamMetricsEvent_IntervalMetric); i { case 0: return &v.state @@ -3160,7 +3434,7 @@ func file_mgmt_proto_init() { return nil } } - file_mgmt_proto_msgTypes[31].Exporter = func(v interface{}, i int) interface{} { + file_mgmt_proto_msgTypes[34].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InspectResponse_InspectValue); i { case 0: return &v.state @@ -3180,7 +3454,7 @@ func file_mgmt_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_mgmt_proto_rawDesc, NumEnums: 6, - NumMessages: 32, + NumMessages: 35, NumExtensions: 0, NumServices: 0, }, diff --git a/common/pb/mgmt_pb/mgmt.proto b/common/pb/mgmt_pb/mgmt.proto index 83ca91772..d8d70c3bf 100644 --- a/common/pb/mgmt_pb/mgmt.proto +++ b/common/pb/mgmt_pb/mgmt.proto @@ -61,6 +61,11 @@ enum ContentType { ValidateRouterDataModelRequestType = 10109; ValidateRouterDataModelResponseType = 10110; ValidateRouterDataModelResultType = 10111; + + ValidateIdentityConnectionStatusesRequestType = 10112; + ValidateIdentityConnectionStatusesResponseType = 10113; + ValidateIdentityConnectionStatusesResultType = 10114; + } enum Header { @@ -290,4 +295,22 @@ message RouterDataModelDetails { string componentName = 3; bool validateSuccess = 4; repeated string errors = 5; +} + +message ValidateIdentityConnectionStatusesRequest { + string routerFilter = 1; +} + +message ValidateIdentityConnectionStatusesResponse { + bool success = 1; + string message = 2; + uint64 componentCount = 3; +} + +message RouterIdentityConnectionStatusesDetails { + string componentType = 1; + string componentId = 2; + string componentName = 3; + bool validateSuccess = 4; + repeated string errors = 5; } \ No newline at end of file diff --git a/controller/agent.go b/controller/agent.go index 48ad46617..db204ce40 100644 --- a/controller/agent.go +++ b/controller/agent.go @@ -113,6 +113,15 @@ func (self *Controller) agentOpRaftAddPeer(m *channel.Message, ch channel.Channe return } + if !self.raftController.IsBootstrapped() { + handler_common.SendOpResult(m, ch, "raft.list", + "Local instance not initialized. If all instances are uninitialized, initialize one first using"+ + " 'ziti agent controller init' then add nodes to that instance. If you have existing initialized nodes,"+ + " use the 'ziti agent cluster add' command on an initialized node, rather than here.", + false) + return + } + addr, found := m.GetStringHeader(AgentAddrHeader) if !found { handler_common.SendOpResult(m, ch, "raft.join", "address not supplied", false) diff --git a/controller/command/command.go b/controller/command/command.go index 2c988755b..0004b08fd 100644 --- a/controller/command/command.go +++ b/controller/command/command.go @@ -54,6 +54,7 @@ type Dispatcher interface { IsLeaderless() bool GetPeers() map[string]channel.Channel GetRateLimiter() rate.RateLimiter + Bootstrap() error } // LocalDispatcher should be used when running a non-clustered system @@ -62,6 +63,10 @@ type LocalDispatcher struct { Limiter rate.RateLimiter } +func (self *LocalDispatcher) Bootstrap() error { + return nil +} + func (self *LocalDispatcher) IsLeaderOrLeaderless() bool { return true } diff --git a/controller/config/config.go b/controller/config/config.go index 7094186f0..bb36b8869 100644 --- a/controller/config/config.go +++ b/controller/config/config.go @@ -211,20 +211,17 @@ func LoadConfig(path string) (*Config, error) { } else { return nil, errors.Errorf("raft dataDir configuration missing") } - if value, found := submap["minClusterSize"]; found { - controllerConfig.Raft.MinClusterSize = uint32(value.(int)) - } - if value, found := submap["bootstrapMembers"]; found { + if value, found := submap["initialMembers"]; found { if lst, ok := value.([]interface{}); ok { for idx, val := range lst { if member, ok := val.(string); ok { - controllerConfig.Raft.BootstrapMembers = append(controllerConfig.Raft.BootstrapMembers, member) + controllerConfig.Raft.InitialMembers = append(controllerConfig.Raft.InitialMembers, member) } else { - return nil, errors.Errorf("invalid bootstrapMembers value '%v'at index %v, should be array", idx, val) + return nil, errors.Errorf("invalid initialMembers value '%v'at index %v, should be array", idx, val) } } } else { - return nil, errors.New("invalid bootstrapMembers value, should be array") + return nil, errors.New("invalid initialMembers value, should be array") } } @@ -396,6 +393,12 @@ func LoadConfig(path string) (*Config, error) { panic("unable to determine trust domain from SPIFFE id: hostname was empty") } + if controllerConfig.Raft != nil { + if err = ValidateSpiffeId(controllerConfig.Id, spiffeId); err != nil { + panic(err) + } + } + //only preserve trust domain spiffeId.Path = "" controllerConfig.SpiffeIdTrustDomain = spiffeId @@ -758,6 +761,17 @@ func GetSpiffeIdFromIdentity(id identity.Identity) (*url.URL, error) { return spiffeId, nil } +func ValidateSpiffeId(id *identity.TokenId, spiffeId *url.URL) error { + if !strings.HasPrefix(spiffeId.Path, "/controller/") { + return fmt.Errorf("invalid SPIFFE id path: %s, should have /controller/ prefix", spiffeId.Path) + } + idInSpiffeId := strings.TrimPrefix(spiffeId.Path, "/controller/") + if idInSpiffeId != id.Token { + return fmt.Errorf("spiffe id '%s', does not match subject identifier '%s'", id.Token, idInSpiffeId) + } + return nil +} + // GetSpiffeIdFromCertChain cycles through a slice of certificates that goes from leaf up CAs. Each certificate // must contain 0 or 1 spiffe:// URI SAN. The first encountered SPIFFE id looking up the chain back to the root CA is returned. // If no SPIFFE id is encountered, nil is returned. Errors are returned for parsing and processing errors only. diff --git a/controller/config/config_edge.go b/controller/config/config_edge.go index dadb63cea..7a91ed619 100644 --- a/controller/config/config_edge.go +++ b/controller/config/config_edge.go @@ -64,6 +64,12 @@ const ( AuthRateLimiterMinSizeValue = 5 AuthRateLimiterMaxSizeValue = 1000 + + DefaultIdentityOnlineStatusScanInterval = time.Minute + MinIdentityOnlineStatusScanInterval = time.Second + + DefaultIdentityOnlineStatusUnknownTimeout = 5 * time.Minute + DefaultIdentityOnlineStatusSource = IdentityStatusSourceHybrid ) type Enrollment struct { @@ -94,15 +100,15 @@ type Api struct { } type EdgeConfig struct { - Enabled bool - Api Api - Enrollment Enrollment - - caPems *bytes.Buffer - caPemsOnce sync.Once - Totp Totp - AuthRateLimiter command.AdaptiveRateLimiterConfig - caCerts []*x509.Certificate + Enabled bool + Api Api + Enrollment Enrollment + IdentityStatusConfig IdentityStatusConfig + caPems *bytes.Buffer + caPemsOnce sync.Once + Totp Totp + AuthRateLimiter command.AdaptiveRateLimiterConfig + caCerts []*x509.Certificate } type HttpTimeouts struct { @@ -122,6 +128,20 @@ func DefaultHttpTimeouts() *HttpTimeouts { return httpTimeouts } +type IdentityStatusSource uint32 + +const ( + IdentityStatusSourceHeartbeats IdentityStatusSource = 1 + IdentityStatusSourceConnectEvents IdentityStatusSource = 2 + IdentityStatusSourceHybrid IdentityStatusSource = 3 +) + +type IdentityStatusConfig struct { + Source IdentityStatusSource + ScanInterval time.Duration + UnknownTimeout time.Duration +} + func NewEdgeConfig() *EdgeConfig { return &EdgeConfig{ Enabled: false, @@ -453,6 +473,57 @@ func (c *EdgeConfig) loadAuthRateLimiterConfig(cfgmap map[interface{}]interface{ return nil } +func (c *EdgeConfig) loadIdentityStatusConfig(cfgmap map[interface{}]interface{}) error { + c.IdentityStatusConfig.ScanInterval = DefaultIdentityOnlineStatusScanInterval + c.IdentityStatusConfig.UnknownTimeout = DefaultIdentityOnlineStatusUnknownTimeout + c.IdentityStatusConfig.Source = DefaultIdentityOnlineStatusSource + + if value, found := cfgmap["identityStatusConfig"]; found { + if submap, ok := value.(map[interface{}]interface{}); ok { + if value, found := submap["scanInterval"]; found { + if interval, err := time.ParseDuration(fmt.Sprintf("%v", value)); err != nil { + pfxlog.Logger().WithError(err).Errorf("invalid value '%v' for identity status config scan interval", value) + } else { + c.IdentityStatusConfig.ScanInterval = interval + } + } + + if c.IdentityStatusConfig.ScanInterval < MinIdentityOnlineStatusScanInterval { + pfxlog.Logger().Errorf("invalid value '%v' for identity status config scan interval, must be at least %s", + c.IdentityStatusConfig.ScanInterval, MinIdentityOnlineStatusScanInterval) + c.IdentityStatusConfig.ScanInterval = MinIdentityOnlineStatusScanInterval + } + + if value, found := submap["unknownTimeout"]; found { + if interval, err := time.ParseDuration(fmt.Sprintf("%v", value)); err != nil { + pfxlog.Logger().WithError(err).Errorf("invalid value '%v' for identity status config unknown timeout", value) + } else { + c.IdentityStatusConfig.UnknownTimeout = interval + } + } + + if value, found := submap["source"]; found { + strVal := fmt.Sprintf("%v", value) + switch strVal { + case "heartbeats": + c.IdentityStatusConfig.Source = IdentityStatusSourceHeartbeats + case "connect-events": + c.IdentityStatusConfig.Source = IdentityStatusSourceConnectEvents + case "hybrid": + c.IdentityStatusConfig.Source = IdentityStatusSourceHybrid + default: + pfxlog.Logger().Errorf("invalid value '%v' for identity status config source, valid values: ['heartbeats', 'connect-events', 'hybrid']", strVal) + } + } + + } else { + return errors.Errorf("invalid type for identityStatusConfig, should be map instead of %T", value) + } + } + + return nil +} + func LoadEdgeConfigFromMap(configMap map[interface{}]interface{}) (*EdgeConfig, error) { edgeConfig := NewEdgeConfig() @@ -490,6 +561,10 @@ func LoadEdgeConfigFromMap(configMap map[interface{}]interface{}) (*EdgeConfig, return nil, err } + if err = edgeConfig.loadIdentityStatusConfig(edgeConfigMap); err != nil { + return nil, err + } + return edgeConfig, nil } diff --git a/controller/config/config_raft.go b/controller/config/config_raft.go index b9064f397..ed1304f64 100644 --- a/controller/config/config_raft.go +++ b/controller/config/config_raft.go @@ -9,9 +9,8 @@ import ( type RaftConfig struct { Recover bool DataDir string - MinClusterSize uint32 AdvertiseAddress transport.Address - BootstrapMembers []string + InitialMembers []string CommandHandlerOptions struct { MaxQueueSize uint16 } diff --git a/controller/controller.go b/controller/controller.go index b49ce752b..c0c835659 100644 --- a/controller/controller.go +++ b/controller/controller.go @@ -22,52 +22,49 @@ import ( "crypto/x509" "encoding/json" "fmt" - "github.com/openziti/foundation/v2/concurrenz" - "github.com/openziti/transport/v2" - "github.com/openziti/transport/v2/tls" - "github.com/openziti/ziti/common/capabilities" - "github.com/openziti/ziti/common/concurrency" - "github.com/openziti/ziti/controller/config" - "github.com/openziti/ziti/controller/env" - "github.com/openziti/ziti/controller/event" - "github.com/openziti/ziti/controller/events" - "github.com/openziti/ziti/controller/handler_peer_ctrl" - "github.com/openziti/ziti/controller/webapis" - "github.com/openziti/ziti/controller/xt_sticky" - "github.com/openziti/ziti/controller/zac" - "math/big" - "os" - "sync" - "sync/atomic" - "time" - - "github.com/openziti/ziti/controller/db" - "github.com/pkg/errors" - "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" "github.com/openziti/channel/v3/protobufs" + "github.com/openziti/foundation/v2/concurrenz" "github.com/openziti/foundation/v2/versions" "github.com/openziti/identity" "github.com/openziti/metrics" "github.com/openziti/storage/boltz" + "github.com/openziti/transport/v2" + "github.com/openziti/transport/v2/tls" "github.com/openziti/xweb/v2" + "github.com/openziti/ziti/common/capabilities" + "github.com/openziti/ziti/common/concurrency" "github.com/openziti/ziti/common/health" fabricMetrics "github.com/openziti/ziti/common/metrics" "github.com/openziti/ziti/common/pb/ctrl_pb" "github.com/openziti/ziti/common/profiler" "github.com/openziti/ziti/controller/command" + "github.com/openziti/ziti/controller/config" + "github.com/openziti/ziti/controller/db" + "github.com/openziti/ziti/controller/env" + "github.com/openziti/ziti/controller/event" + "github.com/openziti/ziti/controller/events" "github.com/openziti/ziti/controller/handler_ctrl" + "github.com/openziti/ziti/controller/handler_peer_ctrl" "github.com/openziti/ziti/controller/network" "github.com/openziti/ziti/controller/raft" "github.com/openziti/ziti/controller/raft/mesh" + "github.com/openziti/ziti/controller/webapis" "github.com/openziti/ziti/controller/xctrl" "github.com/openziti/ziti/controller/xmgmt" "github.com/openziti/ziti/controller/xt" "github.com/openziti/ziti/controller/xt_random" "github.com/openziti/ziti/controller/xt_smartrouting" + "github.com/openziti/ziti/controller/xt_sticky" "github.com/openziti/ziti/controller/xt_weighted" + "github.com/openziti/ziti/controller/zac" + "github.com/pkg/errors" "github.com/sirupsen/logrus" + "math/big" + "os" + "sync" + "sync/atomic" ) type Controller struct { @@ -256,8 +253,8 @@ func NewController(cfg *config.Config, versionProvider versions.VersionProvider) c.initWeb() // need to init web before bootstrapping, so we can provide our endpoints to peers - if c.raftController != nil { - if err := c.raftController.Bootstrap(); err != nil { + if c.raftController != nil && !c.raftController.IsBootstrapped() { + if err = c.TryInitializeRaftFromBoltDb(); err != nil { log.WithError(err).Panic("error bootstrapping raft") } } @@ -595,15 +592,12 @@ func (c *Controller) TryInitializeRaftFromBoltDb() error { func (c *Controller) InitializeRaftFromBoltDb(sourceDbPath string) error { log := pfxlog.Logger() - log.Info("waiting for raft cluster to settle before syncing raft to database") - start := time.Now() - for c.raftController.GetLeaderAddr() == "" { - if time.Since(start) > time.Second*30 { - log.Panic("cannot sync raft to database as cluster has not settled within timeout") - } else { - log.Info("waiting for raft cluster to elect a leader, to allow syncing db to raft") - } - time.Sleep(time.Second) + if c.raftController == nil { + return errors.New("can't initialize non-raft controller using initialize from db") + } + + if c.raftController.IsBootstrapped() { + return errors.New("raft is already bootstrapped, must start with a uninitialized controller") } if _, err := os.Stat(sourceDbPath); err != nil { @@ -642,6 +636,10 @@ func (c *Controller) InitializeRaftFromBoltDb(sourceDbPath string) error { SnapshotSink: c.network.RestoreSnapshot, } + if err = c.raftController.Bootstrap(); err != nil { + return fmt.Errorf("unable to bootstrap cluster (%w)", err) + } + return c.raftController.Dispatch(cmd) } diff --git a/controller/env/appenv.go b/controller/env/appenv.go index 95fbdb62e..7a5d04c7b 100644 --- a/controller/env/appenv.go +++ b/controller/env/appenv.go @@ -213,7 +213,7 @@ func (ae *AppEnv) ValidateServiceAccessToken(token string, apiSessionId *string) } if serviceAccessClaims.ApiSessionId != *apiSessionId { - return nil, fmt.Errorf("invalid api sessoin id, expected %s, got %s", *apiSessionId, serviceAccessClaims.ApiSessionId) + return nil, fmt.Errorf("invalid api session id, expected %s, got %s", *apiSessionId, serviceAccessClaims.ApiSessionId) } } @@ -409,6 +409,10 @@ func (ae *AppEnv) GetCommandDispatcher() command.Dispatcher { return ae.HostController.GetCommandDispatcher() } +func (ae *AppEnv) AddRouterPresenceHandler(h model.RouterPresenceHandler) { + ae.HostController.GetNetwork().AddRouterPresenceHandler(h) +} + type HostController interface { GetConfig() *config.Config GetEnv() *AppEnv @@ -1094,6 +1098,20 @@ func (ae *AppEnv) IsAllowed(responderFunc func(ae *AppEnv, rc *response.RequestC "url": request.URL, }).Warn("could not mark metrics for REST ApiConfig endpoint, request context start time is zero") } + + if rc.ApiSession != nil { + connectEvent := &event.ConnectEvent{ + Namespace: event.ConnectEventNS, + SrcType: event.ConnectSourceIdentity, + DstType: event.ConnectDestinationController, + SrcId: rc.ApiSession.IdentityId, + SrcAddr: rc.Request.RemoteAddr, + DstId: ae.HostController.GetNetwork().GetAppId(), + DstAddr: rc.Request.Host + rc.Request.RequestURI, + Timestamp: time.Now(), + } + ae.GetEventDispatcher().AcceptConnectEvent(connectEvent) + } }) } diff --git a/controller/event/connect.go b/controller/event/connect.go new file mode 100644 index 000000000..41a6ce781 --- /dev/null +++ b/controller/event/connect.go @@ -0,0 +1,54 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package event + +import ( + "time" +) + +type ConnectSource string +type ConnectDestination string + +const ( + ConnectEventNS = "connect" + ConnectSourceRouter ConnectSource = "router" + ConnectSourcePeer ConnectSource = "peer" + ConnectSourceIdentity ConnectSource = "identity" + + ConnectDestinationController ConnectDestination = "ctrl" + ConnectDestinationRouter ConnectDestination = "router" +) + +type ConnectEvent struct { + Namespace string `json:"namespace"` + SrcType ConnectSource `json:"src_type"` + DstType ConnectDestination `json:"dst_type"` + SrcId string `json:"src_id"` + SrcAddr string `json:"src_addr"` + DstId string `json:"dst_id"` + DstAddr string `json:"dst_addr"` + Timestamp time.Time `json:"timestamp"` +} + +type ConnectEventHandler interface { + AcceptConnectEvent(event *ConnectEvent) +} + +type ConnectEventHandlerWrapper interface { + ConnectEventHandler + IsWrapping(value ConnectEventHandler) bool +} diff --git a/controller/event/dispatcher.go b/controller/event/dispatcher.go index d89241f80..42bad05d0 100644 --- a/controller/event/dispatcher.go +++ b/controller/event/dispatcher.go @@ -126,15 +126,17 @@ type Dispatcher interface { ApiSessionEventHandler CircuitEventHandler + ConnectEventHandler + ClusterEventHandler EntityChangeEventHandler LinkEventHandler MetricsEventHandler MetricsMessageHandler RouterEventHandler + SdkEventHandler ServiceEventHandler TerminatorEventHandler UsageEventHandler - ClusterEventHandler } // A Subscription has information to configure an event handler. It contains the EventType to diff --git a/controller/event/dispatcher_mock.go b/controller/event/dispatcher_mock.go index a3334f00c..14516cef3 100644 --- a/controller/event/dispatcher_mock.go +++ b/controller/event/dispatcher_mock.go @@ -27,6 +27,10 @@ var _ Dispatcher = DispatcherMock{} type DispatcherMock struct{} +func (d DispatcherMock) AcceptConnectEvent(event *ConnectEvent) {} + +func (d DispatcherMock) AcceptSdkEvent(event *SdkEvent) {} + func (d DispatcherMock) AcceptApiSessionEvent(event *ApiSessionEvent) {} func (d DispatcherMock) AddApiSessionEventHandler(handler ApiSessionEventHandler) {} diff --git a/controller/event/sdk.go b/controller/event/sdk.go new file mode 100644 index 000000000..0210a0bb3 --- /dev/null +++ b/controller/event/sdk.go @@ -0,0 +1,53 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package event + +import ( + "fmt" + "time" +) + +type SdkEventType string + +const ( + SdkEventsNs = "sdk" + + SdkOnline SdkEventType = "sdk-online" + SdkOffline SdkEventType = "sdk-offline" + SdkStatusUnknown SdkEventType = "sdk-status-unknown" +) + +type SdkEvent struct { + Namespace string `json:"namespace"` + EventType SdkEventType `json:"event_type"` + Timestamp time.Time `json:"timestamp"` + IdentityId string `json:"identity_id"` +} + +func (event *SdkEvent) String() string { + return fmt.Sprintf("%v.%v time=%v identityId=%v", + event.Namespace, event.EventType, event.Timestamp, event.IdentityId) +} + +type SdkEventHandler interface { + AcceptSdkEvent(event *SdkEvent) +} + +type SdkEventHandlerWrapper interface { + SdkEventHandler + IsWrapping(value SdkEventHandler) bool +} diff --git a/controller/events/dispatcher.go b/controller/events/dispatcher.go index 6eede14a9..eb6014493 100644 --- a/controller/events/dispatcher.go +++ b/controller/events/dispatcher.go @@ -63,6 +63,8 @@ func NewDispatcher(closeNotify <-chan struct{}) *Dispatcher { result.RegisterEventTypeFunctions(event.TerminatorEventsNs, result.registerTerminatorEventHandler, result.unregisterTerminatorEventHandler) result.RegisterEventTypeFunctions(event.UsageEventsNs, result.registerUsageEventHandler, result.unregisterUsageEventHandler) result.RegisterEventTypeFunctions(event.ClusterEventsNs, result.registerClusterEventHandler, result.unregisterClusterEventHandler) + result.RegisterEventTypeFunctions(event.ConnectEventNS, result.registerConnectEventHandler, result.unregisterConnectEventHandler) + result.RegisterEventTypeFunctions(event.SdkEventsNs, result.registerSdkEventHandler, result.unregisterSdkEventHandler) result.RegisterEventTypeFunctions(event.ApiSessionEventNS, result.registerApiSessionEventHandler, result.unregisterApiSessionEventHandler) result.RegisterEventTypeFunctions(event.EntityCountEventNS, result.registerEntityCountEventHandler, result.unregisterEntityCountEventHandler) @@ -93,6 +95,8 @@ type Dispatcher struct { usageEventHandlers concurrenz.CopyOnWriteSlice[event.UsageEventHandler] usageEventV3Handlers concurrenz.CopyOnWriteSlice[event.UsageEventV3Handler] clusterEventHandlers concurrenz.CopyOnWriteSlice[event.ClusterEventHandler] + connectEventHandlers concurrenz.CopyOnWriteSlice[event.ConnectEventHandler] + sdkEventHandlers concurrenz.CopyOnWriteSlice[event.SdkEventHandler] apiSessionEventHandlers concurrenz.CopyOnWriteSlice[event.ApiSessionEventHandler] entityCountEventHandlers concurrenz.CopyOnWriteSlice[*entityCountState] diff --git a/controller/events/dispatcher_connect.go b/controller/events/dispatcher_connect.go new file mode 100644 index 000000000..336b6aafc --- /dev/null +++ b/controller/events/dispatcher_connect.go @@ -0,0 +1,62 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package events + +import ( + "github.com/openziti/ziti/controller/event" + "github.com/pkg/errors" + "reflect" +) + +func (self *Dispatcher) AddConnectEventHandler(handler event.ConnectEventHandler) { + self.connectEventHandlers.Append(handler) +} + +func (self *Dispatcher) RemoveConnectEventHandler(handler event.ConnectEventHandler) { + self.connectEventHandlers.DeleteIf(func(val event.ConnectEventHandler) bool { + if val == handler { + return true + } + if w, ok := val.(event.ConnectEventHandlerWrapper); ok { + return w.IsWrapping(handler) + } + return false + }) +} + +func (self *Dispatcher) AcceptConnectEvent(evt *event.ConnectEvent) { + for _, handler := range self.connectEventHandlers.Value() { + go handler.AcceptConnectEvent(evt) + } +} + +func (self *Dispatcher) registerConnectEventHandler(val interface{}, _ map[string]interface{}) error { + handler, ok := val.(event.ConnectEventHandler) + + if !ok { + return errors.Errorf("type %v doesn't implement github.com/openziti/ziti/controller/event/ConnectEventHandler interface.", reflect.TypeOf(val)) + } + + self.AddConnectEventHandler(handler) + return nil +} + +func (self *Dispatcher) unregisterConnectEventHandler(val interface{}) { + if handler, ok := val.(event.ConnectEventHandler); ok { + self.RemoveConnectEventHandler(handler) + } +} diff --git a/controller/events/dispatcher_router.go b/controller/events/dispatcher_router.go index 585ddb0d4..9c8e5c216 100644 --- a/controller/events/dispatcher_router.go +++ b/controller/events/dispatcher_router.go @@ -89,4 +89,25 @@ func (self *routerEventAdapter) routerChange(eventType event.RouterEventType, r } self.Dispatcher.AcceptRouterEvent(evt) + + if eventType == event.RouterOnline { + srcAddr := "" + dstAddr := "" + if ctrl := r.Control; ctrl != nil { + srcAddr = r.Control.Underlay().GetRemoteAddr().String() + dstAddr = r.Control.Underlay().GetLocalAddr().String() + } + + connectEvent := &event.ConnectEvent{ + Namespace: event.ConnectEventNS, + SrcType: event.ConnectSourceRouter, + DstType: event.ConnectDestinationController, + SrcId: r.Id, + SrcAddr: srcAddr, + DstId: self.Dispatcher.network.GetAppId(), + DstAddr: dstAddr, + Timestamp: time.Now(), + } + self.Dispatcher.AcceptConnectEvent(connectEvent) + } } diff --git a/controller/events/dispatcher_sdk.go b/controller/events/dispatcher_sdk.go new file mode 100644 index 000000000..5f7a462f7 --- /dev/null +++ b/controller/events/dispatcher_sdk.go @@ -0,0 +1,62 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package events + +import ( + "github.com/openziti/ziti/controller/event" + "github.com/pkg/errors" + "reflect" +) + +func (self *Dispatcher) AddSdkEventHandler(handler event.SdkEventHandler) { + self.sdkEventHandlers.Append(handler) +} + +func (self *Dispatcher) RemoveSdkEventHandler(handler event.SdkEventHandler) { + self.sdkEventHandlers.DeleteIf(func(val event.SdkEventHandler) bool { + if val == handler { + return true + } + if w, ok := val.(event.SdkEventHandlerWrapper); ok { + return w.IsWrapping(handler) + } + return false + }) +} + +func (self *Dispatcher) AcceptSdkEvent(evt *event.SdkEvent) { + for _, handler := range self.sdkEventHandlers.Value() { + go handler.AcceptSdkEvent(evt) + } +} + +func (self *Dispatcher) registerSdkEventHandler(val interface{}, _ map[string]interface{}) error { + handler, ok := val.(event.SdkEventHandler) + + if !ok { + return errors.Errorf("type %v doesn't implement github.com/openziti/ziti/controller/event/SdkEventHandler interface.", reflect.TypeOf(val)) + } + + self.AddSdkEventHandler(handler) + return nil +} + +func (self *Dispatcher) unregisterSdkEventHandler(val interface{}) { + if handler, ok := val.(event.SdkEventHandler); ok { + self.RemoveSdkEventHandler(handler) + } +} diff --git a/controller/events/formatter.go b/controller/events/formatter.go index 64cf493c9..06318def1 100644 --- a/controller/events/formatter.go +++ b/controller/events/formatter.go @@ -185,6 +185,26 @@ func (event *JsonClusterEvent) Format() ([]byte, error) { return MarshalJson(event) } +type JsonConnectEvent event.ConnectEvent + +func (event *JsonConnectEvent) GetEventType() string { + return "connect" +} + +func (event *JsonConnectEvent) Format() ([]byte, error) { + return MarshalJson(event) +} + +type JsonSdkEvent event.SdkEvent + +func (event *JsonSdkEvent) GetEventType() string { + return "sdk" +} + +func (event *JsonSdkEvent) Format() ([]byte, error) { + return MarshalJson(event) +} + type JsonEntityChangeEvent event.EntityChangeEvent func (event *JsonEntityChangeEvent) GetEventType() string { @@ -277,6 +297,14 @@ func (formatter *JsonFormatter) AcceptClusterEvent(evt *event.ClusterEvent) { formatter.AcceptLoggingEvent((*JsonClusterEvent)(evt)) } +func (formatter *JsonFormatter) AcceptConnectEvent(evt *event.ConnectEvent) { + formatter.AcceptLoggingEvent((*JsonConnectEvent)(evt)) +} + +func (formatter *JsonFormatter) AcceptSdkEvent(evt *event.SdkEvent) { + formatter.AcceptLoggingEvent((*JsonSdkEvent)(evt)) +} + func (formatter *JsonFormatter) AcceptEntityChangeEvent(evt *event.EntityChangeEvent) { formatter.AcceptLoggingEvent((*JsonEntityChangeEvent)(evt)) } diff --git a/controller/handler_edge_ctrl/connect_events.go b/controller/handler_edge_ctrl/connect_events.go new file mode 100644 index 000000000..c4fe875b9 --- /dev/null +++ b/controller/handler_edge_ctrl/connect_events.go @@ -0,0 +1,96 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package handler_edge_ctrl + +import ( + "github.com/michaelquigley/pfxlog" + "github.com/openziti/channel/v3" + "github.com/openziti/ziti/common/pb/edge_ctrl_pb" + "github.com/openziti/ziti/controller/env" + "github.com/openziti/ziti/controller/event" + "google.golang.org/protobuf/proto" + "slices" + "sync" + "time" +) + +type connectEventsHandler struct { + appEnv *env.AppEnv + sync.Mutex +} + +func NewConnectEventsHandler(appEnv *env.AppEnv) channel.TypedReceiveHandler { + return &connectEventsHandler{ + appEnv: appEnv, + } +} + +func (h *connectEventsHandler) ContentType() int32 { + return int32(edge_ctrl_pb.ContentType_ConnectEventsTypes) +} + +func (h *connectEventsHandler) HandleReceive(msg *channel.Message, ch channel.Channel) { + go func() { + // process per-router events in order + h.Lock() + defer h.Unlock() + + req := &edge_ctrl_pb.ConnectEvents{} + routerId := ch.Id() + if err := proto.Unmarshal(msg.Body, req); err != nil { + pfxlog.Logger().WithError(err).Error("could not convert message to ConnectEvents") + } + + identityManager := h.appEnv.Managers.Identity + + if req.FullState { + identityManager.GetConnectionTracker().SyncAllFromRouter(req, ch) + } + + var events []*event.ConnectEvent + for _, identityEvent := range req.Events { + for _, connect := range identityEvent.ConnectTimes { + events = append(events, &event.ConnectEvent{ + Namespace: event.ConnectEventNS, + SrcType: event.ConnectSourceIdentity, + DstType: event.ConnectDestinationRouter, + SrcId: identityEvent.IdentityId, + SrcAddr: connect.SrcAddr, + DstId: routerId, + DstAddr: connect.DstAddr, + Timestamp: time.UnixMilli(connect.ConnectTime), + }) + } + + if !req.FullState { + if identityEvent.IsConnected { + identityManager.GetConnectionTracker().MarkConnected(identityEvent.IdentityId, ch) + } else { + identityManager.GetConnectionTracker().MarkDisconnected(identityEvent.IdentityId, ch) + } + } + } + + slices.SortFunc(events, func(a, b *event.ConnectEvent) int { + return int(a.Timestamp.UnixMilli() - b.Timestamp.UnixMilli()) + }) + + for _, evt := range events { + h.appEnv.GetEventDispatcher().AcceptConnectEvent(evt) + } + }() +} diff --git a/controller/handler_edge_ctrl/create_tunnel_api_session.go b/controller/handler_edge_ctrl/create_tunnel_api_session.go index 789819c92..2f19d0f7a 100644 --- a/controller/handler_edge_ctrl/create_tunnel_api_session.go +++ b/controller/handler_edge_ctrl/create_tunnel_api_session.go @@ -35,11 +35,11 @@ func (self *createApiSessionHandler) Label() string { func (self *createApiSessionHandler) HandleReceive(msg *channel.Message, _ channel.Channel) { req := &edge_ctrl_pb.CreateApiSessionRequest{} if err := proto.Unmarshal(msg.Body, req); err != nil { - logrus.WithField("router", self.ch.Id()).WithError(err).Error("could not unmarshal CreateApiSessionRequest") + logrus.WithField("routerId", self.ch.Id()).WithError(err).Error("could not unmarshal CreateApiSessionRequest") return } - logrus.WithField("router", self.ch.Id()).Debug("create api session request received") + logrus.WithField("routerId", self.ch.Id()).Debug("create api session request received") ctx := &createApiSessionRequestContext{ baseTunnelRequestContext: baseTunnelRequestContext{ @@ -80,9 +80,9 @@ func (self *createApiSessionHandler) createApiSession(ctx *createApiSessionReque responseMsg := channel.NewMessage(int32(edge_ctrl_pb.ContentType_CreateApiSessionResponseType), body) responseMsg.ReplyTo(ctx.msg) if err = self.ch.Send(responseMsg); err != nil { - logrus.WithError(err).Error("failed to send response") + logrus.WithField("routerId", self.ch.Id()).WithError(err).Error("failed to send response") } else { - logrus.WithField("router", self.ch.Id()).Debug("create api session response sent") + logrus.WithField("routerId", self.ch.Id()).Debug("create api session response sent") } } diff --git a/controller/handler_edge_mgmt/init_edge.go b/controller/handler_edge_mgmt/init_edge.go index 7ac5e6e69..0b78ced2c 100644 --- a/controller/handler_edge_mgmt/init_edge.go +++ b/controller/handler_edge_mgmt/init_edge.go @@ -19,9 +19,9 @@ package handler_edge_mgmt import ( "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" + "github.com/openziti/ziti/common/handler_common" "github.com/openziti/ziti/common/pb/edge_mgmt_pb" "github.com/openziti/ziti/controller/env" - "github.com/openziti/ziti/common/handler_common" "google.golang.org/protobuf/proto" ) diff --git a/controller/handler_edge_mgmt/validate_identity_connection_statuses.go b/controller/handler_edge_mgmt/validate_identity_connection_statuses.go new file mode 100644 index 000000000..784a9ae0f --- /dev/null +++ b/controller/handler_edge_mgmt/validate_identity_connection_statuses.go @@ -0,0 +1,249 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package handler_edge_mgmt + +import ( + "encoding/json" + "errors" + "fmt" + "github.com/michaelquigley/pfxlog" + "github.com/openziti/channel/v3" + "github.com/openziti/channel/v3/protobufs" + "github.com/openziti/foundation/v2/concurrenz" + "github.com/openziti/ziti/common/inspect" + "github.com/openziti/ziti/common/pb/ctrl_pb" + "github.com/openziti/ziti/common/pb/mgmt_pb" + "github.com/openziti/ziti/controller/env" + "github.com/openziti/ziti/controller/model" + "github.com/openziti/ziti/controller/network" + "google.golang.org/protobuf/proto" + "strings" + "sync" + "time" +) + +type validateIdentityConnectionStatusesHandler struct { + appEnv *env.AppEnv +} + +func NewValidateIdentityConnectionStatusesHandler(appEnv *env.AppEnv) channel.TypedReceiveHandler { + return &validateIdentityConnectionStatusesHandler{appEnv: appEnv} +} + +func (*validateIdentityConnectionStatusesHandler) ContentType() int32 { + return int32(mgmt_pb.ContentType_ValidateIdentityConnectionStatusesRequestType) +} + +func (handler *validateIdentityConnectionStatusesHandler) getNetwork() *network.Network { + return handler.appEnv.GetHostController().GetNetwork() +} + +func (handler *validateIdentityConnectionStatusesHandler) HandleReceive(msg *channel.Message, ch channel.Channel) { + log := pfxlog.ContextLogger(ch.Label()) + request := &mgmt_pb.ValidateIdentityConnectionStatusesRequest{} + + var err error + + var count int64 + var evalF func() + if err = proto.Unmarshal(msg.Body, request); err == nil { + count, evalF, err = handler.ValidateEdgeConnections(request.RouterFilter, func(detail *mgmt_pb.RouterIdentityConnectionStatusesDetails) { + if !ch.IsClosed() { + if sendErr := protobufs.MarshalTyped(detail).WithTimeout(15 * time.Second).SendAndWaitForWire(ch); sendErr != nil { + log.WithError(sendErr).Error("send of router edge connections detail failed, closing channel") + if closeErr := ch.Close(); closeErr != nil { + log.WithError(closeErr).Error("failed to close channel") + } + } + } else { + log.Info("channel closed, unable to send router egge connections detail") + } + }) + } else { + log.WithError(err).Error("failed to unmarshal request") + return + } + + response := &mgmt_pb.ValidateIdentityConnectionStatusesResponse{ + Success: err == nil, + ComponentCount: uint64(count), + } + if err != nil { + response.Message = fmt.Sprintf("%v: failed to unmarshall request: %v", handler.getNetwork().GetAppId(), err) + } + + if err = protobufs.MarshalTyped(response).ReplyTo(msg).WithTimeout(5 * time.Second).Send(ch); err != nil { + pfxlog.Logger().WithError(err).Error("unexpected error sending response") + } + + if evalF != nil { + evalF() + } +} + +type EdgeConnectionsValidationCallback func(detail *mgmt_pb.RouterIdentityConnectionStatusesDetails) + +func (handler *validateIdentityConnectionStatusesHandler) ValidateEdgeConnections(filter string, cb EdgeConnectionsValidationCallback) (int64, func(), error) { + result, err := handler.appEnv.Managers.Router.BaseList(filter) + if err != nil { + return 0, nil, err + } + + sem := concurrenz.NewSemaphore(10) + + m := handler.appEnv.Managers.Identity.GetIdentityStatusMapCopy() + lock := &sync.Mutex{} + + evalF := func() { + for _, router := range result.Entities { + connectedRouter := handler.appEnv.GetHostController().GetNetwork().GetConnectedRouter(router.Id) + if connectedRouter != nil { + router = connectedRouter + } + sem.Acquire() + go func() { + defer sem.Release() + handler.validRouterEdgeConnections(router, m, lock, cb) + }() + } + } + + count := int64(len(result.Entities)) + return count, evalF, nil +} + +func (handler *validateIdentityConnectionStatusesHandler) validRouterEdgeConnections( + router *model.Router, + m map[string]map[string]channel.Channel, + lock *sync.Mutex, + cb EdgeConnectionsValidationCallback) { + + var identityConnections *inspect.RouterIdentityConnections + + if router.Control != nil && !router.Control.IsClosed() { + request := &ctrl_pb.InspectRequest{RequestedValues: []string{inspect.RouterIdentityConnectionStatusesKey}} + resp := &ctrl_pb.InspectResponse{} + respMsg, err := protobufs.MarshalTyped(request).WithTimeout(time.Minute).SendForReply(router.Control) + if err = protobufs.TypedResponse(resp).Unmarshall(respMsg, err); err != nil { + handler.reportError(router, err, cb) + return + } + + for _, val := range resp.Values { + if val.Name == inspect.RouterIdentityConnectionStatusesKey { + if err = json.Unmarshal([]byte(val.Value), &identityConnections); err != nil { + handler.reportError(router, err, cb) + return + } + } + } + + if identityConnections == nil { + if len(resp.Errors) > 0 { + err = errors.New(strings.Join(resp.Errors, ",")) + handler.reportError(router, err, cb) + return + } + handler.reportError(router, errors.New("no identity in connection details returned from router"), cb) + return + } + } else { + identityConnections = &inspect.RouterIdentityConnections{} + } + + var errList []string + + for identityId, detail := range identityConnections.IdentityConnections { + isConnected := false + for _, conn := range detail.Connections { + if !conn.Closed { + isConnected = true + } + } + + lock.Lock() + connMap := m[identityId] + routerConn := connMap[router.Id] + delete(connMap, router.Id) + if len(connMap) == 0 { + delete(m, identityId) + } + lock.Unlock() + + if isConnected { + if routerConn == nil || routerConn.IsClosed() { + errList = append(errList, fmt.Sprintf("router reports identity %s connected, but controller disagrees", identityId)) + } + } else { + if routerConn != nil && !routerConn.IsClosed() { + errList = append(errList, fmt.Sprintf("router reports identity %s is not connected, but controller disagrees", identityId)) + } + } + + if detail.UnreportedCount > 0 { + errList = append(errList, fmt.Sprintf("identity %s has unreported events", identityId)) + } + + if detail.BeingReportedCount > 0 { + errList = append(errList, fmt.Sprintf("identity %s has events still being reported", identityId)) + } + + if detail.UnreportedStateChanged { + errList = append(errList, fmt.Sprintf("identity %s has unreported state change", identityId)) + } + + if detail.BeingReportedStateChanged { + errList = append(errList, fmt.Sprintf("identity %s has state change being reported", identityId)) + } + } + + lock.Lock() + for identityId, connMap := range m { + if routerConn := connMap[router.Id]; routerConn != nil { + if !routerConn.IsClosed() { + errList = append(errList, fmt.Sprintf("ctrl reports identity %s is connected, but router disagrees", identityId)) + } else if routerConn.GetTimeSinceLastRead() > handler.appEnv.GetConfig().Edge.IdentityStatusConfig.UnknownTimeout { + errList = append(errList, fmt.Sprintf("ctrl still has identity %s router conn, even though time since last read %s > timeout of %s", + identityId, routerConn.GetTimeSinceLastRead(), handler.appEnv.GetConfig().Edge.IdentityStatusConfig.UnknownTimeout)) + } + } + delete(connMap, router.Id) + if len(connMap) == 0 { + delete(m, identityId) + } + } + lock.Unlock() + + details := &mgmt_pb.RouterIdentityConnectionStatusesDetails{ + ComponentType: "router", + ComponentId: router.Id, + ComponentName: router.Name, + ValidateSuccess: true, + Errors: errList, + } + cb(details) +} + +func (handler *validateIdentityConnectionStatusesHandler) reportError(router *model.Router, err error, cb EdgeConnectionsValidationCallback) { + result := &mgmt_pb.RouterIdentityConnectionStatusesDetails{ + ComponentId: router.Id, + ComponentName: router.Name, + ValidateSuccess: false, + Errors: []string{err.Error()}, + } + cb(result) +} diff --git a/controller/handler_peer_ctrl/add_peer.go b/controller/handler_peer_ctrl/add_peer.go index 309750095..29bfd75c1 100644 --- a/controller/handler_peer_ctrl/add_peer.go +++ b/controller/handler_peer_ctrl/add_peer.go @@ -20,9 +20,9 @@ import ( raft2 "github.com/hashicorp/raft" "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" + "github.com/openziti/ziti/common/pb/cmd_pb" "github.com/openziti/ziti/controller/peermsg" "github.com/openziti/ziti/controller/raft" - "github.com/openziti/ziti/common/pb/cmd_pb" "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/protobuf/proto" diff --git a/controller/handler_peer_ctrl/bind.go b/controller/handler_peer_ctrl/bind.go index 9a9408c0c..85b7af71b 100644 --- a/controller/handler_peer_ctrl/bind.go +++ b/controller/handler_peer_ctrl/bind.go @@ -20,10 +20,10 @@ import ( "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" "github.com/openziti/channel/v3/latency" - "github.com/openziti/ziti/controller/network" - "github.com/openziti/ziti/controller/raft" "github.com/openziti/foundation/v2/concurrenz" "github.com/openziti/metrics" + "github.com/openziti/ziti/controller/network" + "github.com/openziti/ziti/controller/raft" "github.com/sirupsen/logrus" "time" ) diff --git a/controller/handler_peer_ctrl/command.go b/controller/handler_peer_ctrl/command.go index 206c9c233..a7cd53bc7 100644 --- a/controller/handler_peer_ctrl/command.go +++ b/controller/handler_peer_ctrl/command.go @@ -19,10 +19,10 @@ package handler_peer_ctrl import ( "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" + "github.com/openziti/foundation/v2/goroutines" "github.com/openziti/ziti/common/metrics" "github.com/openziti/ziti/common/pb/cmd_pb" "github.com/openziti/ziti/controller/raft" - "github.com/openziti/foundation/v2/goroutines" "github.com/sirupsen/logrus" "time" ) diff --git a/controller/handler_peer_ctrl/inspect.go b/controller/handler_peer_ctrl/inspect.go index 02bc4c4f8..0faefa361 100644 --- a/controller/handler_peer_ctrl/inspect.go +++ b/controller/handler_peer_ctrl/inspect.go @@ -70,13 +70,13 @@ type inspectRequestContext struct { } func (context *inspectRequestContext) processLocal() { - for _, requested := range context.request.RequestedValues { - val, err := context.handler.network.Inspect(requested) - if err != nil { - context.appendError(err.Error()) - } else if val != nil { - context.appendValue(requested, *val) - } + result := context.handler.network.Inspections.Inspect(context.handler.network.GetAppId(), context.request.RequestedValues) + for _, value := range result.Results { + context.appendValue(value.Name, value.Value) + } + + for _, err := range result.Errors { + context.appendError(err) } } diff --git a/controller/handler_peer_ctrl/remove_peer.go b/controller/handler_peer_ctrl/remove_peer.go index edd583cbe..5925baff6 100644 --- a/controller/handler_peer_ctrl/remove_peer.go +++ b/controller/handler_peer_ctrl/remove_peer.go @@ -20,9 +20,9 @@ import ( raft2 "github.com/hashicorp/raft" "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" + "github.com/openziti/ziti/common/pb/cmd_pb" "github.com/openziti/ziti/controller/peermsg" "github.com/openziti/ziti/controller/raft" - "github.com/openziti/ziti/common/pb/cmd_pb" "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/protobuf/proto" diff --git a/controller/internal/routes/identity_api_model.go b/controller/internal/routes/identity_api_model.go index dec979b0f..d05039fa2 100644 --- a/controller/internal/routes/identity_api_model.go +++ b/controller/internal/routes/identity_api_model.go @@ -274,28 +274,31 @@ func MapIdentityToRestModel(ae *env.AppEnv, identity *model.Identity) (*rest_mod disabledUntil = &until } + erConnState := identity.EdgeRouterConnectionStatus.String() + ret := &rest_model.IdentityDetail{ - BaseEntity: BaseEntityToRestModel(identity, IdentityLinkFactory), - IsAdmin: &identity.IsAdmin, - IsDefaultAdmin: &identity.IsDefaultAdmin, - Name: &identity.Name, - RoleAttributes: &roleAttributes, - Type: ToEntityRef(identityType.Name, identityType, IdentityTypeLinkFactory), - TypeID: &identityType.Id, - HasEdgeRouterConnection: &identity.HasErConnection, - HasAPISession: &hasApiSession, - DefaultHostingPrecedence: rest_model.TerminatorPrecedence(identity.DefaultHostingPrecedence.String()), - DefaultHostingCost: &cost, - ServiceHostingPrecedences: getRestServiceHostingPrecedences(identity.ServiceHostingPrecedences), - ServiceHostingCosts: getRestServiceHostingCosts(identity.ServiceHostingCosts), - IsMfaEnabled: &isMfaEnabled, - AppData: &appData, - AuthPolicyID: &identity.AuthPolicyId, - AuthPolicy: authPolicyRef, - Disabled: &identity.Disabled, - DisabledAt: disabledAt, - DisabledUntil: disabledUntil, - ExternalID: identity.ExternalId, + BaseEntity: BaseEntityToRestModel(identity, IdentityLinkFactory), + AppData: &appData, + AuthPolicy: authPolicyRef, + AuthPolicyID: &identity.AuthPolicyId, + DefaultHostingCost: &cost, + DefaultHostingPrecedence: rest_model.TerminatorPrecedence(identity.DefaultHostingPrecedence.String()), + Disabled: &identity.Disabled, + DisabledAt: disabledAt, + DisabledUntil: disabledUntil, + EdgeRouterConnectionStatus: &erConnState, + ExternalID: identity.ExternalId, + HasAPISession: &hasApiSession, + HasEdgeRouterConnection: &identity.HasErConnection, + IsAdmin: &identity.IsAdmin, + IsDefaultAdmin: &identity.IsDefaultAdmin, + IsMfaEnabled: &isMfaEnabled, + Name: &identity.Name, + RoleAttributes: &roleAttributes, + ServiceHostingCosts: getRestServiceHostingCosts(identity.ServiceHostingCosts), + ServiceHostingPrecedences: getRestServiceHostingPrecedences(identity.ServiceHostingPrecedences), + Type: ToEntityRef(identityType.Name, identityType, IdentityTypeLinkFactory), + TypeID: &identityType.Id, } fillInfo(ret, identity.EnvInfo, identity.SdkInfo) diff --git a/controller/model/env.go b/controller/model/env.go index 75de6aa76..070f890ec 100644 --- a/controller/model/env.go +++ b/controller/model/env.go @@ -65,4 +65,5 @@ type Env interface { GetApiAddresses() (map[string][]event.ApiAddress, []byte) GetCloseNotifyChannel() <-chan struct{} GetPeerSigners() []*x509.Certificate + AddRouterPresenceHandler(h RouterPresenceHandler) } diff --git a/controller/model/identity_manager.go b/controller/model/identity_manager.go index c61a6a6e9..31c9044d3 100644 --- a/controller/model/identity_manager.go +++ b/controller/model/identity_manager.go @@ -21,16 +21,21 @@ import ( "errors" "fmt" "github.com/michaelquigley/pfxlog" + "github.com/openziti/channel/v3" "github.com/openziti/foundation/v2/errorz" "github.com/openziti/metrics" "github.com/openziti/sdk-golang/ziti" "github.com/openziti/storage/boltz" "github.com/openziti/ziti/common/eid" + "github.com/openziti/ziti/common/inspect" "github.com/openziti/ziti/common/pb/cmd_pb" "github.com/openziti/ziti/common/pb/edge_cmd_pb" + "github.com/openziti/ziti/common/pb/edge_ctrl_pb" "github.com/openziti/ziti/controller/change" "github.com/openziti/ziti/controller/command" + "github.com/openziti/ziti/controller/config" "github.com/openziti/ziti/controller/db" + "github.com/openziti/ziti/controller/event" "github.com/openziti/ziti/controller/fields" "github.com/openziti/ziti/controller/models" cmap "github.com/orcaman/concurrent-map/v2" @@ -55,6 +60,8 @@ type IdentityManager struct { baseEntityManager[*Identity, *db.Identity] updateSdkInfoTimer metrics.Timer identityStatusMap *identityStatusMap + connections *ConnectionTracker + statusSource config.IdentityStatusSource } func NewIdentityManager(env Env) *IdentityManager { @@ -62,6 +69,8 @@ func NewIdentityManager(env Env) *IdentityManager { baseEntityManager: newBaseEntityManager[*Identity, *db.Identity](env, env.GetStores().Identity), updateSdkInfoTimer: env.GetMetricsRegistry().Timer("identity.update-sdk-info"), identityStatusMap: newIdentityStatusMap(IdentityActiveIntervalSeconds * time.Second), + connections: newConnectionTracker(env), + statusSource: env.GetConfig().Edge.IdentityStatusConfig.Source, } manager.impl = manager @@ -185,16 +194,6 @@ func (self *IdentityManager) ReadOneByQuery(query string) (*Identity, error) { } func (self *IdentityManager) InitializeDefaultAdmin(username, password, name string) error { - identity, err := self.ReadDefaultAdmin() - - if err != nil && !boltz.IsErrNotFoundErr(err) { - return err - } - - if identity != nil { - return errors.New("already initialized: Ziti Edge default admin already defined") - } - if len(username) < minDefaultAdminUsernameLength { return errorz.NewFieldError(fmt.Sprintf("username must be at least %v characters", minDefaultAdminUsernameLength), "username", username) } @@ -221,6 +220,20 @@ func (self *IdentityManager) InitializeDefaultAdmin(username, password, name str return err } + identity, err := self.ReadDefaultAdmin() + + if err != nil && !boltz.IsErrNotFoundErr(err) { + return err + } + + if identity != nil { + return errors.New("already initialized: Ziti Edge default admin already defined") + } + + if err = self.env.GetManagers().Dispatcher.Bootstrap(); err != nil { + return fmt.Errorf("unable to bootstrap command dispatcher (%w)", err) + } + identityId := eid.New() authenticatorId := eid.New() @@ -469,6 +482,10 @@ func (self *IdentityManager) PatchInfo(identity *Identity, changeCtx *change.Con return err } +func (self *IdentityManager) GetConnectionTracker() *ConnectionTracker { + return self.connections +} + // SetHasErConnection will register an identity as having an ER connection. The registration has a TTL depending on // how the status map was configured. func (self *IdentityManager) SetHasErConnection(identityId string) { @@ -477,7 +494,13 @@ func (self *IdentityManager) SetHasErConnection(identityId string) { // HasErConnection will return true if the supplied identity id has a current an active ER connection registered. func (self *IdentityManager) HasErConnection(id string) bool { - return self.identityStatusMap.HasEdgeRouterConnection(id) + if self.statusSource == config.IdentityStatusSourceConnectEvents { + return self.connections.GetIdentityOnlineState(id) == IdentityStateOnline + } + if self.statusSource == config.IdentityStatusSourceHeartbeats { + return self.identityStatusMap.HasEdgeRouterConnection(id) + } + return self.connections.GetIdentityOnlineState(id) == IdentityStateOnline || self.identityStatusMap.HasEdgeRouterConnection(id) } func (self *IdentityManager) VisitIdentityAuthenticatorFingerprints(tx *bbolt.Tx, identityId string, visitor func(string) bool) (bool, error) { @@ -564,6 +587,20 @@ func (self *IdentityManager) Enable(identityId string, ctx *change.Context) erro }, fieldMap, ctx) } +func (self *IdentityManager) GetIdentityStatusMapCopy() map[string]map[string]channel.Channel { + result := map[string]map[string]channel.Channel{} + for entry := range self.connections.connections.IterBuffered() { + routerMap := map[string]channel.Channel{} + entry.Val.Lock() + for routerId, ch := range entry.Val.routers { + routerMap[routerId] = ch + } + entry.Val.Unlock() + result[entry.Key] = routerMap + } + return result +} + func (self *IdentityManager) IdentityToProtobuf(entity *Identity) (*edge_cmd_pb.Identity, error) { tags, err := edge_cmd_pb.EncodeTags(entity.Tags) if err != nil { @@ -866,6 +903,270 @@ func (statusMap *identityStatusMap) start() { }() } +type IdentityOnlineState uint32 + +func (self IdentityOnlineState) String() string { + if self == IdentityStateOffline { + return "offline" + } + if self == IdentityStateOnline { + return "online" + } + return "unknown" +} + +const ( + IdentityStateOffline IdentityOnlineState = 0 + IdentityStateOnline IdentityOnlineState = 1 + IdentityStateUnknown IdentityOnlineState = 2 +) + +type identityConnections struct { + sync.RWMutex + routers map[string]channel.Channel + lastReportedState IdentityOnlineState +} + +func (self *identityConnections) calculateState() IdentityOnlineState { + // if any router is connected, the identity is online + for _, router := range self.routers { + if !router.IsClosed() { + return IdentityStateOnline + } + } + + // if the identity is reported as connected to one or more routers, but they're all offline, + // then the identity state is unknown + if len(self.routers) > 0 { + return IdentityStateUnknown + } + + // if the identity has no router connections, it's off-line + return IdentityStateOffline +} + +func newConnectionTracker(env Env) *ConnectionTracker { + result := &ConnectionTracker{ + connections: cmap.New[*identityConnections](), + eventDispatcher: env.GetEventDispatcher(), + scanInterval: env.GetConfig().Edge.IdentityStatusConfig.ScanInterval, + unknownTimeout: env.GetConfig().Edge.IdentityStatusConfig.UnknownTimeout, + closeNotify: env.GetCloseNotifyChannel(), + } + if result.scanInterval < 5*time.Second { + result.scanInterval = 5 * time.Second + } + go result.runScanLoop() + return result +} + +type ConnectionTracker struct { + connections cmap.ConcurrentMap[string, *identityConnections] + scanInterval time.Duration + unknownTimeout time.Duration + eventDispatcher event.Dispatcher + closeNotify <-chan struct{} +} + +func (self *ConnectionTracker) runScanLoop() { + ticker := time.NewTicker(self.scanInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + self.ScanForDisconnectedRouters() + case <-self.closeNotify: + return + } + } +} + +func (self *ConnectionTracker) ScanForDisconnectedRouters() { + for entry := range self.connections.IterBuffered() { + var toRemove []channel.Channel + entry.Val.RLock() + for _, routerCh := range entry.Val.routers { + if routerCh.IsClosed() && routerCh.GetTimeSinceLastRead() > self.unknownTimeout { + toRemove = append(toRemove, routerCh) + } + } + entry.Val.RUnlock() + + for _, routerCh := range toRemove { + self.MarkDisconnected(entry.Key, routerCh) + } + + if len(toRemove) == 0 { + var reportState *IdentityOnlineState + + entry.Val.Lock() + lastReportedState := entry.Val.lastReportedState + currentState := entry.Val.calculateState() + if lastReportedState != currentState { + reportState = ¤tState + } + entry.Val.Unlock() + + if reportState != nil { + self.SendSdkOnlineStatusChangeEvent(entry.Key, *reportState) + } + } + + entry.Val.Lock() + if len(entry.Val.routers) == 0 { + self.connections.RemoveCb(entry.Key, func(key string, v *identityConnections, exists bool) bool { + if v != nil { + return len(v.routers) == 0 + } + return true + }) + } + entry.Val.Unlock() + } +} + +func (self *ConnectionTracker) MarkConnected(identityId string, ch channel.Channel) { + pfxlog.Logger().WithField("identityId", identityId).WithField("routerId", ch.Id()).Trace("marking identity connected to router") + var postUpsertCallback func() + self.connections.Upsert(identityId, nil, func(exist bool, valueInMap *identityConnections, newValue *identityConnections) *identityConnections { + if valueInMap == nil { + valueInMap = &identityConnections{ + routers: map[string]channel.Channel{}, + } + } + + if ch.IsClosed() { + return valueInMap + } + + valueInMap.Lock() + oldState := valueInMap.calculateState() + valueInMap.routers[ch.Id()] = ch + newState := valueInMap.calculateState() + lastReportedState := valueInMap.lastReportedState + valueInMap.lastReportedState = newState + valueInMap.Unlock() + + if newState != oldState || newState != lastReportedState { + postUpsertCallback = func() { + self.SendSdkOnlineStatusChangeEvent(identityId, newState) + } + } + return valueInMap + }) + + if postUpsertCallback != nil { + postUpsertCallback() + } +} + +func (self *ConnectionTracker) MarkDisconnected(identityId string, ch channel.Channel) { + pfxlog.Logger().WithField("identityId", identityId).WithField("routerId", ch.Id()).Trace("marking identity disconnected from router") + var postUpsertCallback func() + self.connections.Upsert(identityId, nil, func(exist bool, valueInMap *identityConnections, newValue *identityConnections) *identityConnections { + if valueInMap == nil { + return &identityConnections{ + routers: map[string]channel.Channel{}, + } + } + + valueInMap.Lock() + oldState := valueInMap.calculateState() + + current := valueInMap.routers[ch.Id()] + if current == nil || current == ch || current.IsClosed() { + delete(valueInMap.routers, ch.Id()) + } + + newState := valueInMap.calculateState() + lastReportedState := valueInMap.lastReportedState + valueInMap.lastReportedState = newState + valueInMap.Unlock() + + if newState != oldState || newState != lastReportedState { + postUpsertCallback = func() { + self.SendSdkOnlineStatusChangeEvent(identityId, newState) + } + } + return valueInMap + }) + + if postUpsertCallback != nil { + postUpsertCallback() + } +} + +func (self *ConnectionTracker) SendSdkOnlineStatusChangeEvent(identityId string, state IdentityOnlineState) { + var eventType event.SdkEventType + if state == IdentityStateOffline { + eventType = event.SdkOffline + } else if state == IdentityStateOnline { + eventType = event.SdkOnline + } else if state == IdentityStateUnknown { + eventType = event.SdkStatusUnknown + } + + self.eventDispatcher.AcceptSdkEvent(&event.SdkEvent{ + Namespace: event.SdkEventsNs, + EventType: eventType, + Timestamp: time.Now(), + IdentityId: identityId, + }) +} + +func (self *ConnectionTracker) GetIdentityOnlineState(identityId string) IdentityOnlineState { + val, _ := self.connections.Get(identityId) + if val == nil { + return IdentityStateOffline + } + val.RLock() + defer val.RUnlock() + return val.calculateState() +} + +func (self *ConnectionTracker) SyncAllFromRouter(state *edge_ctrl_pb.ConnectEvents, ch channel.Channel) { + m := map[string]bool{} + for _, identityState := range state.Events { + m[identityState.IdentityId] = identityState.IsConnected + if identityState.IsConnected { + self.MarkConnected(identityState.IdentityId, ch) + } + } + + for _, identityId := range self.connections.Keys() { + if connected := m[identityId]; !connected { + self.MarkDisconnected(identityId, ch) + } + } +} + +func (self *ConnectionTracker) Inspect() *inspect.CtrlIdentityConnections { + result := &inspect.CtrlIdentityConnections{ + Connections: map[string]*inspect.CtrlIdentityConnectionDetail{}, + ScanInterval: self.scanInterval.String(), + } + + for entry := range self.connections.IterBuffered() { + entry.Val.Lock() + val := &inspect.CtrlIdentityConnectionDetail{ + ConnectedRouters: map[string]*inspect.CtrlRouterConnection{}, + LastReportedState: entry.Val.lastReportedState.String(), + } + result.Connections[entry.Key] = val + for routerId, ch := range entry.Val.routers { + val.ConnectedRouters[routerId] = &inspect.CtrlRouterConnection{ + RouterId: ch.Id(), + Closed: ch.IsClosed(), + TimeSinceLastWrite: ch.GetTimeSinceLastRead().String(), + } + } + entry.Val.Unlock() + } + + return result +} + type UpdateServiceConfigsCmd struct { manager *IdentityManager identityId string diff --git a/controller/model/identity_model.go b/controller/model/identity_model.go index 03a154c07..309095b74 100644 --- a/controller/model/identity_model.go +++ b/controller/model/identity_model.go @@ -76,25 +76,26 @@ func (self *SdkInfo) Equals(other *SdkInfo) bool { type Identity struct { models.BaseEntity - Name string - IdentityTypeId string - IsDefaultAdmin bool - IsAdmin bool - RoleAttributes []string - EnvInfo *EnvInfo - SdkInfo *SdkInfo - HasErConnection bool - DefaultHostingPrecedence ziti.Precedence - DefaultHostingCost uint16 - ServiceHostingPrecedences map[string]ziti.Precedence - ServiceHostingCosts map[string]uint16 - AppData map[string]interface{} - AuthPolicyId string - ExternalId *string - Disabled bool - DisabledAt *time.Time - DisabledUntil *time.Time - ServiceConfigs map[string]map[string]string + Name string + IdentityTypeId string + IsDefaultAdmin bool + IsAdmin bool + RoleAttributes []string + EnvInfo *EnvInfo + SdkInfo *SdkInfo + HasErConnection bool + EdgeRouterConnectionStatus IdentityOnlineState + DefaultHostingPrecedence ziti.Precedence + DefaultHostingCost uint16 + ServiceHostingPrecedences map[string]ziti.Precedence + ServiceHostingCosts map[string]uint16 + AppData map[string]interface{} + AuthPolicyId string + ExternalId *string + Disabled bool + DisabledAt *time.Time + DisabledUntil *time.Time + ServiceConfigs map[string]map[string]string } func (entity *Identity) toBoltEntityForCreate(_ *bbolt.Tx, env Env) (*db.Identity, error) { @@ -279,6 +280,7 @@ func (entity *Identity) fillFrom(env Env, _ *bbolt.Tx, boltIdentity *db.Identity entity.IsAdmin = boltIdentity.IsAdmin entity.RoleAttributes = boltIdentity.RoleAttributes entity.HasErConnection = env.GetManagers().Identity.HasErConnection(entity.Id) + entity.EdgeRouterConnectionStatus = env.GetManagers().Identity.connections.GetIdentityOnlineState(boltIdentity.Id) entity.DefaultHostingPrecedence = boltIdentity.DefaultHostingPrecedence entity.DefaultHostingCost = boltIdentity.DefaultHostingCost entity.ServiceHostingPrecedences = boltIdentity.ServiceHostingPrecedences diff --git a/controller/model/router_manager.go b/controller/model/router_manager.go index ad235fa51..723e4a391 100644 --- a/controller/model/router_manager.go +++ b/controller/model/router_manager.go @@ -49,6 +49,11 @@ const ( RouterDequiesceFlag uint32 = 2 ) +type RouterPresenceHandler interface { + RouterConnected(r *Router) + RouterDisconnected(r *Router) +} + func NewRouter(id, name, fingerprint string, cost uint16, noTraversal bool) *Router { if name == "" { name = id diff --git a/controller/model/testing.go b/controller/model/testing.go index dfc90a785..a3305350c 100644 --- a/controller/model/testing.go +++ b/controller/model/testing.go @@ -49,6 +49,7 @@ type TestContext struct { metricsRegistry metrics.Registry closeNotify chan struct{} dispatcher command.Dispatcher + eventDispatcher event.Dispatcher } func (ctx *TestContext) GetEnrollmentJwtSigner() (jwtsigner.Signer, error) { @@ -56,7 +57,7 @@ func (ctx *TestContext) GetEnrollmentJwtSigner() (jwtsigner.Signer, error) { } func (ctx *TestContext) GetEventDispatcher() event.Dispatcher { - panic("implement me") + return ctx.eventDispatcher } func (self *TestContext) GetCloseNotifyChannel() <-chan struct{} { @@ -178,6 +179,8 @@ func (self *TestContext) GetCommandDispatcher() command.Dispatcher { return self.dispatcher } +func (self *TestContext) AddRouterPresenceHandler(RouterPresenceHandler) {} + func NewTestContext(t testing.TB) *TestContext { fabricTestContext := db.NewTestContext(t) ctx := &TestContext{ @@ -188,6 +191,7 @@ func NewTestContext(t testing.TB) *TestContext { EncodeDecodeCommands: true, Limiter: command.NoOpRateLimiter{}, }, + eventDispatcher: event.DispatcherMock{}, } ctx.TestContext.Init() diff --git a/controller/network/handler.go b/controller/network/handler.go deleted file mode 100644 index 15c87a69e..000000000 --- a/controller/network/handler.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - Copyright NetFoundry Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -package network - -import "github.com/openziti/ziti/controller/model" - -type RouterPresenceHandler interface { - RouterConnected(r *model.Router) - RouterDisconnected(r *model.Router) -} diff --git a/controller/network/inspect.go b/controller/network/inspect.go index 9116a325c..89cd98314 100644 --- a/controller/network/inspect.go +++ b/controller/network/inspect.go @@ -17,14 +17,20 @@ package network import ( + "encoding/json" "fmt" "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" "github.com/openziti/channel/v3/protobufs" "github.com/openziti/foundation/v2/concurrenz" + "github.com/openziti/foundation/v2/debugz" + "github.com/openziti/ziti/common/inspect" "github.com/openziti/ziti/common/pb/ctrl_pb" "github.com/openziti/ziti/controller/model" + "github.com/openziti/ziti/controller/raft" + "github.com/openziti/ziti/controller/xt" "regexp" + "strings" "sync" "time" ) @@ -124,12 +130,7 @@ func (ctx *inspectRequestContext) inspectLocal() { ctx.waitGroup.AddNotifier(notifier) go func() { for _, requested := range ctx.requestedValues { - result, err := ctx.network.Inspect(requested) - if err != nil { - ctx.appendError(ctx.network.GetAppId(), err.Error()) - } else if result != nil { - ctx.appendValue(ctx.network.GetAppId(), requested, *result) - } + ctx.InspectLocal(requested) } close(notifier) }() @@ -138,6 +139,87 @@ func (ctx *inspectRequestContext) inspectLocal() { } } +func (ctx *inspectRequestContext) InspectLocal(name string) { + lc := strings.ToLower(name) + + if lc == "stackdump" { + result := debugz.GenerateStack() + ctx.handleLocalStringResponse(name, &result, nil) + } else if strings.HasPrefix(lc, "metrics") { + msg := ctx.network.metricsRegistry.Poll() + ctx.handleLocalJsonResponse(name, msg) + } else if lc == "config" { + if rc, ok := ctx.network.config.(renderConfig); ok { + val, err := rc.RenderJsonConfig() + ctx.handleLocalStringResponse(name, &val, err) + } + } else if lc == "cluster-config" { + if src, ok := ctx.network.Dispatcher.(renderConfig); ok { + val, err := src.RenderJsonConfig() + ctx.handleLocalStringResponse(name, &val, err) + } + } else if lc == "connected-routers" { + var result []map[string]any + for _, r := range ctx.network.Router.AllConnected() { + status := map[string]any{} + status["Id"] = r.Id + status["Name"] = r.Name + status["Version"] = r.VersionInfo.Version + status["ConnectTime"] = r.ConnectTime.Format(time.RFC3339) + result = append(result, status) + } + ctx.handleLocalJsonResponse(name, result) + } else if lc == "connected-peers" { + if raftController, ok := ctx.network.Dispatcher.(*raft.Controller); ok { + members, err := raftController.ListMembers() + if err != nil { + ctx.appendError(ctx.network.GetAppId(), err.Error()) + return + } + ctx.handleLocalJsonResponse(name, members) + } + } else if lc == "router-messaging" { + routerMessagingState, err := ctx.network.RouterMessaging.Inspect() + if err != nil { + ctx.appendError(ctx.network.GetAppId(), err.Error()) + return + } + ctx.handleLocalJsonResponse(name, routerMessagingState) + } else if strings.HasPrefix(lc, "terminator-costs") { + state := &inspect.TerminatorCostDetails{} + xt.GlobalCosts().IterCosts(func(terminatorId string, cost xt.Cost) { + state.Terminators = append(state.Terminators, cost.Inspect(terminatorId)) + }) + ctx.handleLocalJsonResponse(name, state) + } else if lc == inspect.RouterIdentityConnectionStatusesKey { + result := ctx.network.env.GetManagers().Identity.GetConnectionTracker().Inspect() + ctx.handleLocalJsonResponse(name, result) + } else { + for _, inspectTarget := range ctx.network.inspectionTargets.Value() { + if handled, val, err := inspectTarget(lc); handled { + ctx.handleLocalStringResponse(name, val, err) + } + } + } +} + +func (ctx *inspectRequestContext) handleLocalJsonResponse(key string, val interface{}) { + js, err := json.Marshal(val) + if err != nil { + ctx.appendError(ctx.network.GetAppId(), fmt.Errorf("failed to marshall %s to json (%w)", key, err).Error()) + } else { + ctx.appendValue(ctx.network.GetAppId(), key, string(js)) + } +} + +func (ctx *inspectRequestContext) handleLocalStringResponse(key string, val *string, err error) { + if err != nil { + ctx.appendError(ctx.network.GetAppId(), err.Error()) + } else if val != nil { + ctx.appendValue(ctx.network.GetAppId(), key, *val) + } +} + func (ctx *inspectRequestContext) inspectRouter(router *model.Router) { log := pfxlog.Logger(). WithField("appRegex", ctx.appRegex). diff --git a/controller/network/network.go b/controller/network/network.go index 3dd73892d..c61193f15 100644 --- a/controller/network/network.go +++ b/controller/network/network.go @@ -32,7 +32,6 @@ import ( "github.com/openziti/ziti/controller/event" "github.com/openziti/ziti/controller/idgen" "github.com/openziti/ziti/controller/model" - "github.com/openziti/ziti/controller/raft" "google.golang.org/protobuf/proto" "math" "os" @@ -94,7 +93,7 @@ type Network struct { sequence *sequence.Sequence eventDispatcher event.Dispatcher traceController trace.Controller - routerPresenceHandlers []RouterPresenceHandler + routerPresenceHandlers concurrenz.CopyOnWriteSlice[model.RouterPresenceHandler] capabilities []string closeNotify <-chan struct{} watchdogCh chan struct{} @@ -354,7 +353,7 @@ func (network *Network) ConnectRouter(r *model.Router) { time.AfterFunc(250*time.Millisecond, network.notifyAssembleAndClean) - for _, h := range network.routerPresenceHandlers { + for _, h := range network.routerPresenceHandlers.Value() { go h.RouterConnected(r) } go network.ValidateTerminators(r) @@ -446,7 +445,7 @@ func (network *Network) DisconnectRouter(r *model.Router) { // 2: remove Router network.Router.MarkDisconnected(r) - for _, h := range network.routerPresenceHandlers { + for _, h := range network.routerPresenceHandlers.Value() { h.RouterDisconnected(r) } @@ -901,8 +900,8 @@ func (network *Network) setLinks(path *model.Path) error { return nil } -func (network *Network) AddRouterPresenceHandler(h RouterPresenceHandler) { - network.routerPresenceHandlers = append(network.routerPresenceHandlers, h) +func (network *Network) AddRouterPresenceHandler(h model.RouterPresenceHandler) { + network.routerPresenceHandlers.Append(h) } func (network *Network) Run() { @@ -1230,92 +1229,6 @@ type renderConfig interface { RenderJsonConfig() (string, error) } -func (network *Network) Inspect(name string) (*string, error) { - lc := strings.ToLower(name) - - if lc == "stackdump" { - result := debugz.GenerateStack() - return &result, nil - } else if strings.HasPrefix(lc, "metrics") { - msg := network.metricsRegistry.Poll() - js, err := json.Marshal(msg) - if err != nil { - return nil, errors.Wrap(err, "failed to marshal metrics to json") - } - result := string(js) - return &result, nil - } else if lc == "config" { - if rc, ok := network.config.(renderConfig); ok { - val, err := rc.RenderJsonConfig() - if err != nil { - return nil, errors.Wrap(err, "failed to marshal config to json") - } - return &val, nil - } - } else if lc == "cluster-config" { - if src, ok := network.Dispatcher.(renderConfig); ok { - val, err := src.RenderJsonConfig() - return &val, err - } - } else if lc == "connected-routers" { - var result []map[string]any - for _, r := range network.Router.AllConnected() { - status := map[string]any{} - status["Id"] = r.Id - status["Name"] = r.Name - status["Version"] = r.VersionInfo.Version - status["ConnectTime"] = r.ConnectTime.Format(time.RFC3339) - result = append(result, status) - } - val, err := json.Marshal(result) - strVal := string(val) - return &strVal, err - } else if lc == "connected-peers" { - if raftController, ok := network.Dispatcher.(*raft.Controller); ok { - members, err := raftController.ListMembers() - if err != nil { - return nil, err - } - result, err := json.Marshal(members) - if err != nil { - return nil, fmt.Errorf("failed to marshall cluster member list to json (%w)", err) - } - resultStr := string(result) - return &resultStr, nil - } - } else if lc == "router-messaging" { - routerMessagingState, err := network.RouterMessaging.Inspect() - if err != nil { - return nil, err - } - result, err := json.Marshal(routerMessagingState) - if err != nil { - return nil, fmt.Errorf("failed to marshall router messaging state to json (%w)", err) - } - resultStr := string(result) - return &resultStr, nil - } else if strings.HasPrefix(lc, "terminator-costs") { - state := inspect.TerminatorCostDetails{} - xt.GlobalCosts().IterCosts(func(terminatorId string, cost xt.Cost) { - state.Terminators = append(state.Terminators, cost.Inspect(terminatorId)) - }) - result, err := json.Marshal(state) - if err != nil { - return nil, fmt.Errorf("failed to marshall terminator cost state to json (%w)", err) - } - resultStr := string(result) - return &resultStr, nil - } else { - for _, inspectTarget := range network.inspectionTargets.Value() { - if handled, val, err := inspectTarget(lc); handled { - return val, err - } - } - } - - return nil, nil -} - func (network *Network) routerDeleted(routerId string) { circuits := network.GetAllCircuits() for _, circuit := range circuits { diff --git a/controller/raft/fsm.go b/controller/raft/fsm.go index dd52d198f..05b6d9a82 100644 --- a/controller/raft/fsm.go +++ b/controller/raft/fsm.go @@ -19,13 +19,14 @@ package raft import ( "bytes" "compress/gzip" + "fmt" "github.com/hashicorp/raft" "github.com/michaelquigley/pfxlog" + "github.com/openziti/storage/boltz" "github.com/openziti/ziti/controller/change" "github.com/openziti/ziti/controller/command" "github.com/openziti/ziti/controller/db" event2 "github.com/openziti/ziti/controller/event" - "github.com/openziti/storage/boltz" "github.com/pkg/errors" "github.com/sirupsen/logrus" "go.etcd.io/bbolt" @@ -78,8 +79,12 @@ func (self *BoltDbFsm) GetDb() boltz.Db { } func (self *BoltDbFsm) loadCurrentIndex() (uint64, error) { + return self.loadDbIndex(self.db) +} + +func (self *BoltDbFsm) loadDbIndex(zitiDb boltz.Db) (uint64, error) { var result uint64 - err := self.db.View(func(tx *bbolt.Tx) error { + err := zitiDb.View(func(tx *bbolt.Tx) error { result = db.LoadCurrentRaftIndex(tx) return nil }) @@ -193,48 +198,135 @@ func (self *BoltDbFsm) Snapshot() (raft.FSMSnapshot, error) { } func (self *BoltDbFsm) Restore(snapshot io.ReadCloser) error { + var currentSnapshotId string + var currentIndex uint64 + if self.db != nil { - if err := self.db.Close(); err != nil { - return err + snapshotId, _ := self.db.GetSnapshotId() + if snapshotId != nil { + currentSnapshotId = *snapshotId } + currentIndex, _ = self.loadCurrentIndex() } logrus.Info("restoring from snapshot") - backup := self.dbPath + ".backup" - err := os.Rename(self.dbPath, backup) - if err != nil { + tmpPath := self.dbPath + ".stage" + if err := self.restoreSnapshotDbFile(tmpPath, snapshot); err != nil { return err } - dbFile, err := os.OpenFile(self.dbPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600) + newSnapshotId, newIndex, err := self.GetSnapshotMetadata(tmpPath) if err != nil { return err } - gzReader, err := gzip.NewReader(snapshot) - if err != nil { - return errors.Wrapf(err, "unable to create gz reader for reading raft snapshot during restore") + log := pfxlog.Logger(). + WithField("currentSnapshotId", currentSnapshotId). + WithField("newSnapshotId", newSnapshotId). + WithField("currentIndex", currentIndex). + WithField("newIndex", newIndex) + + if newIndex > currentIndex { + log.Info("new index is greater than current index, restoring snapshot") + } else { + log.Info("snapshot index is <= current index, no need to restore snapshot") + if err = os.Remove(tmpPath); err != nil { + pfxlog.Logger().WithError(err).WithField("path", tmpPath).Error("failed to remove temporary snapshot db file") + } + return nil } - _, err = io.Copy(dbFile, gzReader) - if err != nil { - _ = os.Remove(self.dbPath) - if renameErr := os.Rename(backup, self.dbPath); renameErr != nil { - logrus.WithError(renameErr).Error("failed to move ziti db back to original location") + if self.db != nil { + if err := self.db.Close(); err != nil { + return err } - return err + } + + backup := self.dbPath + ".backup" + if err = os.Rename(self.dbPath, backup); err != nil { + return fmt.Errorf("failed to copy existing db to backup path (%w)", err) + } + + if err = os.Rename(tmpPath, self.dbPath); err != nil { + return fmt.Errorf("failed to copy snapshot db to primary db location (%w)", err) } // if we're not initializing from a snapshot at startup, restart if self.indexTracker.Index() > 0 { + log.Info("restored snapshot to initialized system, restart required. exiting") os.Exit(0) } self.db, err = db.Open(self.dbPath) + if err == nil { + log.Info("restored snapshot to uninitialized system, ok to continue") + } return err } +func (self *BoltDbFsm) restoreSnapshotDbFile(path string, snapshot io.ReadCloser) error { + dbFile, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600) + if err != nil { + return err + } + + success := false + + defer func() { + if err = dbFile.Close(); err != nil { + pfxlog.Logger().WithError(err).WithField("path", path).Error("failed to close temporary snapshot db file") + } + + if !success { + if err = os.Remove(path); err != nil { + pfxlog.Logger().WithError(err).WithField("path", path).Error("failed to remove temporary snapshot db file") + } + } + }() + + gzReader, err := gzip.NewReader(snapshot) + if err != nil { + return errors.Wrapf(err, "unable to create gz reader for reading raft snapshot during restore") + } + + if _, err = io.Copy(dbFile, gzReader); err != nil { + return err + } + + success = true + return nil +} + +func (self *BoltDbFsm) GetSnapshotMetadata(path string) (string, uint64, error) { + newDb, err := db.Open(path) + if err != nil { + return "", 0, err + } + + defer func() { + if err = newDb.Close(); err != nil { + pfxlog.Logger().WithError(err).WithField("path", path).Error("error closing snapshot db") + } + }() + + snapshotIdP, err := newDb.GetSnapshotId() + if err != nil { + return "", 0, err + } + var snapshotId string + if snapshotIdP != nil { + snapshotId = *snapshotIdP + } + + idx, err := self.loadDbIndex(newDb) + if err != nil { + return "", 0, err + } + + return snapshotId, idx, nil +} + type boltSnapshot struct { snapshotId string snapshotData []byte diff --git a/controller/raft/mesh/mesh.go b/controller/raft/mesh/mesh.go index 9ab950523..52c08da3e 100644 --- a/controller/raft/mesh/mesh.go +++ b/controller/raft/mesh/mesh.go @@ -395,7 +395,7 @@ func (self *impl) GetOrConnectPeer(address string, timeout time.Duration) (*Peer binding.AddReceiveHandlerF(RaftDisconnectType, peer.handleReceiveDisconnect) binding.AddCloseHandler(peer) - return self.PeerConnected(peer) + return self.PeerConnected(peer, true) }) if _, err = channel.NewChannel(ChannelTypeMesh, dialer, bindHandler, channel.DefaultOptions()); err != nil { @@ -486,7 +486,7 @@ func ExtractSpiffeId(certs []*x509.Certificate) (string, error) { return "", errors.New("invalid controller certificate, no controller SPIFFE ID in cert") } -func (self *impl) PeerConnected(peer *Peer) error { +func (self *impl) PeerConnected(peer *Peer, dial bool) error { self.lock.Lock() defer self.lock.Unlock() if self.Peers[peer.Address] != nil { @@ -509,6 +509,28 @@ func (self *impl) PeerConnected(peer *Peer) error { }) self.eventDispatcher.AcceptClusterEvent(evt) + + if !dial { + srcAddr := "" + dstAddr := "" + if ch := peer.Channel; ch != nil { + srcAddr = ch.Underlay().GetRemoteAddr().String() + dstAddr = ch.Underlay().GetLocalAddr().String() + } + connectEvent := &event.ConnectEvent{ + Namespace: event.ConnectEventNS, + SrcType: event.ConnectSourcePeer, + DstType: event.ConnectDestinationController, + SrcId: string(peer.Id), + SrcAddr: srcAddr, + DstId: self.id.Token, + DstAddr: dstAddr, + Timestamp: time.Now(), + } + + self.eventDispatcher.AcceptConnectEvent(connectEvent) + } + return nil } @@ -637,7 +659,7 @@ func (self *impl) AcceptUnderlay(underlay channel.Underlay) error { binding.AddReceiveHandlerF(RaftDataType, peer.handleReceiveData) binding.AddReceiveHandlerF(RaftDisconnectType, peer.handleReceiveDisconnect) binding.AddCloseHandler(peer) - return self.PeerConnected(peer) + return self.PeerConnected(peer, false) }) _, err := channel.NewChannelWithUnderlay(ChannelTypeMesh, underlay, bindHandler, channel.DefaultOptions()) diff --git a/controller/raft/mesh/mesh_test.go b/controller/raft/mesh/mesh_test.go index 5b8a274db..f98e2cc65 100644 --- a/controller/raft/mesh/mesh_test.go +++ b/controller/raft/mesh/mesh_test.go @@ -59,7 +59,7 @@ func Test_AddPeer_PassesReadonlyWhenVersionsMatch(t *testing.T) { p := &Peer{Version: testVersion("1")} - assert.NoError(t, m.PeerConnected(p)) + assert.NoError(t, m.PeerConnected(p, true)) assert.Equal(t, false, m.readonly.Load(), "Expected readonly to be false, got ", m.readonly.Load()) } @@ -72,7 +72,7 @@ func Test_AddPeer_TurnsReadonlyWhenVersionsDoNotMatch(t *testing.T) { p := &Peer{Version: testVersion("dne")} - assert.NoError(t, m.PeerConnected(p)) + assert.NoError(t, m.PeerConnected(p, true)) assert.Equal(t, true, m.readonly.Load(), "Expected readonly to be true, got ", m.readonly.Load()) } diff --git a/controller/raft/raft.go b/controller/raft/raft.go index f2533171a..e70f1946a 100644 --- a/controller/raft/raft.go +++ b/controller/raft/raft.go @@ -139,7 +139,6 @@ type Controller struct { Fsm *BoltDbFsm bootstrapped atomic.Bool clusterLock sync.Mutex - servers []raft.Server closeNotify <-chan struct{} indexTracker IndexTracker migrationMgr MigrationManager @@ -198,6 +197,10 @@ func (self *Controller) IsLeaderless() bool { return self.GetLeaderAddr() == "" } +func (self *Controller) IsBootstrapped() bool { + return self.bootstrapped.Load() || self.GetRaft().LastIndex() > 0 +} + func (self *Controller) IsReadOnlyMode() bool { return self.Mesh.IsReadOnly() } @@ -689,10 +692,6 @@ func (self *Controller) Bootstrap() error { return err } - if err := self.addConfiguredBootstrapMembers(); err != nil { - return err - } - req := &cmd_pb.AddPeerRequest{ Addr: string(self.Mesh.GetAdvertiseAddr()), Id: self.env.GetId().Token, @@ -702,41 +701,39 @@ func (self *Controller) Bootstrap() error { if err := self.Join(req); err != nil { return err } - } - return nil -} -func (self *Controller) addBootstrapServer(server raft.Server) { - for _, current := range self.servers { - if current.ID == server.ID { - pfxlog.Logger().Infof("already have bootstrap member with id %v, not adding again", server.ID) - return + start := time.Now() + firstCheckPassed := false + for { + // make sure this is in a reasonably steady state by waiting a bit longer and checking twice + if self.isLeader.Load() { + if firstCheckPassed { + break + } else { + firstCheckPassed = true + } + } + if time.Since(start) > time.Second*10 { + return fmt.Errorf("node did not bootstrap in time") + } + time.Sleep(100 * time.Millisecond) } - } - pfxlog.Logger().Infof("adding bootstrap server id=%v, addr=%v", server.ID, server.Address) - self.servers = append(self.servers, server) + go self.addConfiguredInitialMembers() + } + return nil } -func (self *Controller) addConfiguredBootstrapMembers() error { - for _, bootstrapMember := range self.Config.BootstrapMembers { +func (self *Controller) addConfiguredInitialMembers() { + for _, bootstrapMember := range self.Config.InitialMembers { _, err := transport.ParseAddress(bootstrapMember) if err != nil { - return errors.Wrapf(err, "unable to parse address for bootstrap member [%v]", bootstrapMember) + pfxlog.Logger().WithError(err).Errorf("unable to parse address for bootstrap member [%v], it will be ignored", bootstrapMember) + continue } - if id, addr, err := self.Mesh.GetPeerInfo(bootstrapMember, time.Second*5); err != nil { - pfxlog.Logger().WithError(err).Errorf("unable to get id for bootstrap member [%v]", bootstrapMember) - go self.retryBootstrapMember(bootstrapMember) - } else { - self.addBootstrapServer(raft.Server{ - Suffrage: raft.Voter, - ID: id, - Address: addr, - }) - } + go self.retryBootstrapMember(bootstrapMember) } - return nil } func (self *Controller) retryBootstrapMember(bootstrapMember string) { @@ -744,15 +741,8 @@ func (self *Controller) retryBootstrapMember(bootstrapMember string) { defer ticker.Stop() for { - <-ticker.C - - // If we've bootstrapped, exit out - if self.bootstrapped.Load() { - return - } - if id, addr, err := self.Mesh.GetPeerInfo(bootstrapMember, time.Second*5); err != nil { - pfxlog.Logger().WithError(err).Errorf("unable to get id for bootstrap member [%v]", bootstrapMember) + pfxlog.Logger().WithError(err).Errorf("unable to get id for bootstrap member [%v], will retry", bootstrapMember) } else { req := &cmd_pb.AddPeerRequest{ Addr: string(addr), @@ -761,9 +751,11 @@ func (self *Controller) retryBootstrapMember(bootstrapMember string) { } if err = self.Join(req); err == nil { + pfxlog.Logger().WithError(err).Errorf("erroring adding bootstrap member [%s], stopping attempts to join it to the cluster", bootstrapMember) return } } + <-ticker.C } } @@ -789,37 +781,23 @@ func (self *Controller) Join(req *cmd_pb.AddPeerRequest) error { suffrage = raft.Nonvoter } - self.addBootstrapServer(raft.Server{ + return self.tryBootstrap(raft.Server{ ID: raft.ServerID(req.Id), Address: raft.ServerAddress(req.Addr), Suffrage: suffrage, }) - - return self.tryBootstrap() } -func (self *Controller) tryBootstrap() error { +func (self *Controller) tryBootstrap(servers ...raft.Server) error { log := pfxlog.Logger() - votingCount := uint32(0) - for _, server := range self.servers { - if server.Suffrage == raft.Voter { - votingCount++ - } - } - - if votingCount >= self.Config.MinClusterSize { - log.Infof("min cluster member count met, bootstrapping cluster") - f := self.GetRaft().BootstrapCluster(raft.Configuration{Servers: self.servers}) - if err := f.Error(); err != nil { - return errors.Wrapf(err, "failed to bootstrap cluster") - } - self.bootstrapped.Store(true) - log.Info("raft cluster bootstrap complete") - if err := self.migrationMgr.TryInitializeRaftFromBoltDb(); err != nil { - panic(err) - } + log.Infof("bootstrapping cluster") + f := self.GetRaft().BootstrapCluster(raft.Configuration{Servers: servers}) + if err := f.Error(); err != nil { + return errors.Wrapf(err, "failed to bootstrap cluster") } + self.bootstrapped.Store(true) + log.Info("raft cluster bootstrap complete") return nil } diff --git a/controller/server/controller.go b/controller/server/controller.go index 7d706bedd..1f684e2de 100644 --- a/controller/server/controller.go +++ b/controller/server/controller.go @@ -138,6 +138,7 @@ func (c *Controller) GetCtrlHandlers(binding channel.Binding) []channel.TypedRec handler_edge_ctrl.NewTunnelHealthEventHandler(c.AppEnv, ch), handler_edge_ctrl.NewExtendEnrollmentHandler(c.AppEnv), handler_edge_ctrl.NewExtendEnrollmentVerifyHandler(c.AppEnv), + handler_edge_ctrl.NewConnectEventsHandler(c.AppEnv), } result = append(result, c.AppEnv.Broker.GetReceiveHandlers()...) @@ -149,6 +150,7 @@ func (c *Controller) GetMgmtHandlers() []channel.TypedReceiveHandler { return []channel.TypedReceiveHandler{ handler_edge_mgmt.NewInitEdgeHandler(c.AppEnv), handler_edge_mgmt.NewValidateRouterDataModelHandler(c.AppEnv), + handler_edge_mgmt.NewValidateIdentityConnectionStatusesHandler(c.AppEnv), } } @@ -272,7 +274,10 @@ func (c *Controller) checkEdgeInitialized() { now := time.Now() if now.Sub(lastWarn) > time.Minute { - log.Warn("the Ziti Edge has not been initialized, no default admin exists. Please run 'ziti agent controller init' to configure the default admin'") + log.Warnf("the Ziti Edge has not been initialized, no default admin exists. Add this node to a cluster using "+ + "'ziti agent cluster add %s' against an existing cluster member, or if this is the bootstrap node, run "+ + "'ziti agent controller init' to configure the default admin and bootstrap the cluster", + (*c.AppEnv.HostController.GetEnv().GetConfig().Ctrl.Options.AdvertiseAddress).String()) lastWarn = now } time.Sleep(time.Second) diff --git a/controller/sync_strats/marshal.go b/controller/sync_strats/marshal.go index 7d6857a68..6a9877c3e 100644 --- a/controller/sync_strats/marshal.go +++ b/controller/sync_strats/marshal.go @@ -43,6 +43,7 @@ func apiSessionToProtoWithTx(tx *bbolt.Tx, ae *env.AppEnv, token, identityId, ap Token: token, CertFingerprints: fingerprints, Id: apiSessionId, + IdentityId: identityId, }, nil } diff --git a/doc/ha/ctrl1.yml b/doc/ha/ctrl1.yml index 939c5b79f..0b502f54e 100644 --- a/doc/ha/ctrl1.yml +++ b/doc/ha/ctrl1.yml @@ -1,8 +1,7 @@ v: 3 raft: - minClusterSize: 3 dataDir: ./data/ctrl1 - bootstrapMembers: + initialMembers: - tls:127.0.0.1:6363 - tls:127.0.0.1:6464 @@ -16,14 +15,15 @@ ctrl: options: advertiseAddress: tls:localhost:6262 -#events: -# jsonLogger: -# subscriptions: +events: + jsonLogger: + subscriptions: + - type: connect # - type: fabric.cluster -# handler: -# type: file -# format: json -# path: /tmp/ziti-events.log + handler: + type: file + format: json + path: /tmp/ziti-events.log edge: api: diff --git a/doc/ha/ctrl2.yml b/doc/ha/ctrl2.yml index ffce4c134..46587d782 100644 --- a/doc/ha/ctrl2.yml +++ b/doc/ha/ctrl2.yml @@ -1,7 +1,6 @@ v: 3 raft: - minClusterSize: 3 dataDir: ./data/ctrl2 identity: diff --git a/doc/ha/ctrl3.yml b/doc/ha/ctrl3.yml index 6077096cf..02d8e914e 100644 --- a/doc/ha/ctrl3.yml +++ b/doc/ha/ctrl3.yml @@ -1,7 +1,6 @@ v: 3 raft: - minClusterSize: 3 dataDir: ./data/ctrl3 identity: diff --git a/etc/ctrl.with.edge.yml b/etc/ctrl.with.edge.yml index 1b222c37a..dc21f6cf1 100644 --- a/etc/ctrl.with.edge.yml +++ b/etc/ctrl.with.edge.yml @@ -79,9 +79,11 @@ ctrl: # connections. The value of newListener must be resolvable both via DNS and validate via certificates #newListener: tls:localhost:6262 -#events: -# jsonLogger: -# subscriptions: +events: + jsonLogger: + subscriptions: + - type: connect + - type: sdk # - type: entityChange # include: # - services @@ -103,10 +105,10 @@ ctrl: # - type: services # - type: edge.entityCounts # interval: 5s -# handler: -# type: file -# format: json -# path: /tmp/ziti-events.log + handler: + type: file + format: json + path: /tmp/ziti-events.log # usageLogger: # subscriptions: # - type: fabric.usage diff --git a/go.mod b/go.mod index 888706455..2eb517c6c 100644 --- a/go.mod +++ b/go.mod @@ -50,18 +50,18 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/natefinch/lumberjack v2.0.0+incompatible github.com/openziti/agent v1.0.18 - github.com/openziti/channel/v3 v3.0.6 + github.com/openziti/channel/v3 v3.0.7 github.com/openziti/cobra-to-md v1.0.1 - github.com/openziti/edge-api v0.26.34 + github.com/openziti/edge-api v0.26.35 github.com/openziti/foundation/v2 v2.0.49 - github.com/openziti/identity v1.0.86 + github.com/openziti/identity v1.0.87 github.com/openziti/jwks v1.0.6 github.com/openziti/metrics v1.2.58 github.com/openziti/runzmd v1.0.51 github.com/openziti/sdk-golang v0.23.44 github.com/openziti/secretstream v0.1.25 github.com/openziti/storage v0.3.2 - github.com/openziti/transport/v2 v2.0.147 + github.com/openziti/transport/v2 v2.0.148 github.com/openziti/x509-claims v1.0.3 github.com/openziti/xweb/v2 v2.1.3 github.com/openziti/ziti-db-explorer v1.1.3 diff --git a/go.sum b/go.sum index 00043e24e..05accd08e 100644 --- a/go.sum +++ b/go.sum @@ -570,18 +570,18 @@ github.com/openziti-incubator/cf v0.0.3 h1:JKs55DbaIxl87nI/Ra/3DHMiz5iaPpu8JjsuN github.com/openziti-incubator/cf v0.0.3/go.mod h1:6abCY06bCjKmK2I9kohij+cp9uXIPFiFwSCNZPdMk8E= github.com/openziti/agent v1.0.18 h1:+MP1AXGresJPcbhbsFdElpTWqrQW+VZOLya0V+/mGbE= github.com/openziti/agent v1.0.18/go.mod h1:HET46hghk8ahnVt/3mfVjmnL4NLNVZGnqvrQC3PbIn8= -github.com/openziti/channel/v3 v3.0.6 h1:HnV7im5cdOWltw5aYOoJb8TsZ+JLsDGOqwU/fhLm7e4= -github.com/openziti/channel/v3 v3.0.6/go.mod h1:YV+xmG4kptP48BAVMKJS+Pbuw0j3hWoWervh8m2aYpA= +github.com/openziti/channel/v3 v3.0.7 h1:Xi29/KszQcB0Zs466EQYEwORdw+1BBL9TWFlFh70QPw= +github.com/openziti/channel/v3 v3.0.7/go.mod h1:cpaBPj8bMLTDzuQFFxAvbEWcbzFN2XMwAqK33iRLa0M= github.com/openziti/cobra-to-md v1.0.1 h1:WRinNoIRmwWUSJm+pSNXMjOrtU48oxXDZgeCYQfVXxE= github.com/openziti/cobra-to-md v1.0.1/go.mod h1:FjCpk/yzHF7/r28oSTNr5P57yN5VolpdAtS/g7KNi2c= github.com/openziti/dilithium v0.3.5 h1:+envGNzxc3OyVPiuvtxivQmCsOjdZjtOMLpQBeMz7eM= github.com/openziti/dilithium v0.3.5/go.mod h1:XONq1iK6te/WwNzkgZHfIDHordMPqb0hMwJ8bs9EfSk= -github.com/openziti/edge-api v0.26.34 h1:73OcSpEeE2pdLKxPH2cGwDi8YcROWRGJfR+BAFRPGIE= -github.com/openziti/edge-api v0.26.34/go.mod h1:sYHVpm26Jr1u7VooNJzTb2b2nGSlmCHMnbGC8XfWSng= +github.com/openziti/edge-api v0.26.35 h1:32ILwMAPCQrf5ZVIR8IQO5AQwblIM60yD1+ABw48vXU= +github.com/openziti/edge-api v0.26.35/go.mod h1:sYHVpm26Jr1u7VooNJzTb2b2nGSlmCHMnbGC8XfWSng= github.com/openziti/foundation/v2 v2.0.49 h1:aQ5I/lMhkHQ6urhRpLwrWP+7YtoeUitCfY/wub+nOqo= github.com/openziti/foundation/v2 v2.0.49/go.mod h1:tFk7wg5WE/nDDur5jSVQTROugKDXQkFvmqRSV4pvWp0= -github.com/openziti/identity v1.0.86 h1:VmOdm+NCw0ZnPprR0nOMX43I2O+Q+b/FULNTnvcXXiw= -github.com/openziti/identity v1.0.86/go.mod h1:beIXWNDImEjZn93XPOorJzyuQCQUYOvKFQ0fWhLN2qM= +github.com/openziti/identity v1.0.87 h1:v0NnaDee/5GkPGSoG+2XTl0+0b3BDsm1R6EkkBmK+Zw= +github.com/openziti/identity v1.0.87/go.mod h1:beIXWNDImEjZn93XPOorJzyuQCQUYOvKFQ0fWhLN2qM= github.com/openziti/jwks v1.0.6 h1:PR+9OVaMO8oHEoVQmHqeUBExWwLWyODEGJQK2DXHaqE= github.com/openziti/jwks v1.0.6/go.mod h1:t4xxq8vlXGsPn29kiQVnZBBDDnEoOFqtJoHibkJunQQ= github.com/openziti/metrics v1.2.58 h1:AbHSTMKHP/o6r6fh7a08c486Y/5f5xjkZQbcyn3w1tM= @@ -594,8 +594,8 @@ github.com/openziti/secretstream v0.1.25 h1:40gHKcAcoXqKs0J7Tz1jTAmPoMXmMn4HP3Mg github.com/openziti/secretstream v0.1.25/go.mod h1:zgBcyN7h/zLBIWeqSrWwlOGOMQW51oQGYYlkiArR6Ec= github.com/openziti/storage v0.3.2 h1:etRAT2asJvV1gKgj/eRu3st7AO0TKgDagsEpDdIj/l0= github.com/openziti/storage v0.3.2/go.mod h1:yTv6Rqs8Rk6nMPUD+96VXI5eWhOARTNLV0OPmgiK8I4= -github.com/openziti/transport/v2 v2.0.147 h1:YXNIu17SKecdfxQwLDeyF2bIkE6h4XOrlyzMdVeHuJY= -github.com/openziti/transport/v2 v2.0.147/go.mod h1:ovDQhNomg+Vl8cdHCeqG7HrRIsedkfhJQ1/QAm8Ktic= +github.com/openziti/transport/v2 v2.0.148 h1:2xNPyWp3eY31NsqzldRSpVuRnLpybTU4MkNPltPg80c= +github.com/openziti/transport/v2 v2.0.148/go.mod h1:n9QrYf+nudf4aSU4hvtC5WPTxqPJT6Vpg+vOSk8ae4I= github.com/openziti/x509-claims v1.0.3 h1:HNdQ8Nf1agB3lBs1gahcO6zfkeS4S5xoQ2/PkY4HRX0= github.com/openziti/x509-claims v1.0.3/go.mod h1:Z0WIpBm6c4ecrpRKrou6Gk2wrLWxJO/+tuUwKh8VewE= github.com/openziti/xweb/v2 v2.1.3 h1:smHMs6BCdSF3LB3KMHvR8YcNYKESJjM9LBfHi958/2E= diff --git a/router/config.go b/router/config.go index 0b2009ee8..1adeeb755 100644 --- a/router/config.go +++ b/router/config.go @@ -25,6 +25,7 @@ import ( "io" "os" "path/filepath" + "strings" "time" "github.com/michaelquigley/pfxlog" @@ -81,6 +82,22 @@ const ( CtrlRateLimiterMetricWorkTimer = "ctrl_limiter.work_timer" CtrlHaMapKey = "ha" + + ConnectEventsMapKey = "connectEvents" + + DefaultConnectEventsEnabled = true + + DefaultConnectEventsBatchInterval = 3 * time.Second + MinConnectEventsBatchInterval = 250 * time.Millisecond + MaxConnectEventsBatchInterval = 10 * time.Minute + + DefaultConnectEventsMaxQueuedEvents = 100_000 + MinConnectEventsMaxQueuedEvents = 0 + MaxConnectEventsMaxQueuedEvents = 10_000_000 + + DefaultConnectEventsFullSyncInterval = 5 * time.Minute + MinConnectEventsFullSyncInterval = time.Second + MaxConnectEventsFullSyncInterval = 24 * time.Hour ) // internalConfigKeys is used to distinguish internally defined configuration vs file configuration @@ -165,10 +182,11 @@ type Config struct { Ha struct { Enabled bool } - Proxy *transport.ProxyConfiguration - Plugins []string - src map[interface{}]interface{} - path string + ConnectEvents env.ConnectEventsConfig + Proxy *transport.ProxyConfiguration + Plugins []string + src map[interface{}]interface{} + path string } func (config *Config) CurrentCtrlAddress() string { @@ -810,6 +828,86 @@ func LoadConfig(path string) (*Config, error) { } } + cfg.ConnectEvents.Enabled = DefaultConnectEventsEnabled + cfg.ConnectEvents.BatchInterval = DefaultConnectEventsBatchInterval + cfg.ConnectEvents.FullSyncInterval = DefaultConnectEventsFullSyncInterval + cfg.ConnectEvents.MaxQueuedEvents = DefaultConnectEventsMaxQueuedEvents + + if value, found := cfgmap[ConnectEventsMapKey]; found { + if connectEvents, ok := value.(map[interface{}]interface{}); !ok { + pfxlog.Logger().Warn("invalid xgress edge listener options value: connectEvents value should be map") + } else { + if value, found := connectEvents["enabled"]; found { + enabled := strings.EqualFold("true", fmt.Sprintf("%v", value)) + cfg.ConnectEvents.Enabled = enabled + } + + if value, found := connectEvents["batchInterval"]; found { + if strVal, ok := value.(string); ok { + interval, err := time.ParseDuration(strVal) + if err != nil { + pfxlog.Logger().WithError(err).Warn("invalid value: connectEvents.batchInterval value should be a valid duration") + } else { + cfg.ConnectEvents.BatchInterval = interval + } + } else { + pfxlog.Logger().Warn("invalid value: connectEvents.batchInterval value should be a string representing a duration") + } + } + + if value, found := connectEvents["fullSyncInterval"]; found { + if strVal, ok := value.(string); ok { + interval, err := time.ParseDuration(strVal) + if err != nil { + pfxlog.Logger().WithError(err).Warn("invalid value: connectEvents.fullSyncInterval value should be a valid duration") + } else { + cfg.ConnectEvents.FullSyncInterval = interval + } + } else { + pfxlog.Logger().Warn("invalid value: connectEvents.fullSyncInterval value should be a string representing a duration") + } + } + + if value, found := connectEvents["maxQueuedEvents"]; found { + if intVal, ok := value.(int); ok { + cfg.ConnectEvents.MaxQueuedEvents = int64(intVal) + } else { + pfxlog.Logger().Warn("invalid value: connectEvents.fullSyncInterval should be a positive integer value") + } + } + } + } + + if cfg.ConnectEvents.BatchInterval < MinConnectEventsBatchInterval { + pfxlog.Logger().Warnf("connectEvents.batchInterval less than allowed minimum of %s", MinConnectEventsBatchInterval.String()) + cfg.ConnectEvents.BatchInterval = MinConnectEventsBatchInterval + } + + if cfg.ConnectEvents.BatchInterval > MaxConnectEventsBatchInterval { + pfxlog.Logger().Warnf("connectEvents.batchInterval greater than allowed maximum of %s", MaxConnectEventsBatchInterval.String()) + cfg.ConnectEvents.BatchInterval = MaxConnectEventsBatchInterval + } + + if cfg.ConnectEvents.FullSyncInterval < MinConnectEventsBatchInterval { + pfxlog.Logger().Warnf("connectEvents.fullSyncInterval less than allowed minimum of %s", MinConnectEventsFullSyncInterval.String()) + cfg.ConnectEvents.FullSyncInterval = MinConnectEventsFullSyncInterval + } + + if cfg.ConnectEvents.FullSyncInterval > MaxConnectEventsBatchInterval { + pfxlog.Logger().Warnf("connectEvents.fullSyncInterval greater than allowed maximum of %s", MaxConnectEventsFullSyncInterval.String()) + cfg.ConnectEvents.FullSyncInterval = MaxConnectEventsFullSyncInterval + } + + if cfg.ConnectEvents.MaxQueuedEvents < MinConnectEventsMaxQueuedEvents { + pfxlog.Logger().Warnf("connectEvents.maxQueuedEvents less than allowed minimum of %d", MinConnectEventsMaxQueuedEvents) + cfg.ConnectEvents.MaxQueuedEvents = MinConnectEventsMaxQueuedEvents + } + + if cfg.ConnectEvents.MaxQueuedEvents > MaxConnectEventsMaxQueuedEvents { + pfxlog.Logger().Warnf("connectEvents.maxQueuedEvents greater than allowed maximum of %d", MaxConnectEventsMaxQueuedEvents) + cfg.ConnectEvents.MaxQueuedEvents = MaxConnectEventsMaxQueuedEvents + } + return cfg, nil } diff --git a/router/env/ctrls.go b/router/env/ctrls.go index 8b3318336..5a03e6129 100644 --- a/router/env/ctrls.go +++ b/router/env/ctrls.go @@ -38,6 +38,7 @@ type NetworkControllers interface { GetAll() map[string]NetworkController GetNetworkController(ctrlId string) NetworkController AnyCtrlChannel() channel.Channel + GetIfResponsive(ctrlId string) (channel.Channel, bool) AllResponsiveCtrlChannels() []channel.Channel AnyValidCtrlChannel() channel.Channel GetCtrlChannel(ctrlId string) channel.Channel @@ -195,6 +196,17 @@ func (self *networkControllers) AllResponsiveCtrlChannels() []channel.Channel { return channels } +func (self *networkControllers) GetIfResponsive(ctrlId string) (channel.Channel, bool) { + ch := self.ctrls.Get(ctrlId) + if ch == nil { + return nil, false + } + if ch.IsConnected() && !ch.IsUnresponsive() { + return ch.Channel(), true + } + return nil, true +} + func (self *networkControllers) AnyValidCtrlChannel() channel.Channel { delay := 10 * time.Millisecond for { diff --git a/router/env/env.go b/router/env/env.go index 4ccd4c32f..b55fc90d7 100644 --- a/router/env/env.go +++ b/router/env/env.go @@ -26,6 +26,7 @@ import ( "github.com/openziti/ziti/common" "github.com/openziti/ziti/router/xgress" "github.com/openziti/ziti/router/xlink" + "time" ) type RouterEnv interface { @@ -44,4 +45,12 @@ type RouterEnv interface { GetCtrlRateLimiter() rate.AdaptiveRateLimitTracker GetVersionInfo() versions.VersionProvider GetRouterDataModel() *common.RouterDataModel + GetConnectEventsConfig() *ConnectEventsConfig +} + +type ConnectEventsConfig struct { + Enabled bool + BatchInterval time.Duration + MaxQueuedEvents int64 + FullSyncInterval time.Duration } diff --git a/router/handler_ctrl/inspect.go b/router/handler_ctrl/inspect.go index 9a5b5a076..3eeab4134 100644 --- a/router/handler_ctrl/inspect.go +++ b/router/handler_ctrl/inspect.go @@ -22,6 +22,7 @@ import ( "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" "github.com/openziti/foundation/v2/debugz" + "github.com/openziti/ziti/common/inspect" "github.com/openziti/ziti/common/pb/ctrl_pb" "github.com/openziti/ziti/router/env" "github.com/openziti/ziti/router/forwarder" @@ -133,6 +134,21 @@ func (context *inspectRequestContext) processLocal() { } else if lc == "router-controllers" { result := context.handler.env.GetNetworkControllers().Inspect() context.handleJsonResponse(requested, result) + } else if lc == inspect.RouterIdentityConnectionStatusesKey { + factory, _ := xgress.GlobalRegistry().Factory("edge") + if factory == nil { + context.appendError("no xgress factory configured for edge binding") + continue + } + + inspectable, ok := factory.(xgress.Inspectable) + if !ok { + context.appendError("edge factory is not of type Inspectable") + continue + } + + result := inspectable.Inspect(lc, time.Second) + context.handleJsonResponse(requested, result) } } } diff --git a/router/router.go b/router/router.go index c53d3664f..ca8b2cbe8 100644 --- a/router/router.go +++ b/router/router.go @@ -171,6 +171,10 @@ func (self *Router) IsHaEnabled() bool { return self.config.Ha.Enabled } +func (self *Router) GetConnectEventsConfig() *env.ConnectEventsConfig { + return &self.config.ConnectEvents +} + func Create(config *Config, versionProvider versions.VersionProvider) *Router { closeNotify := make(chan struct{}) diff --git a/router/state/manager.go b/router/state/manager.go index 5dece6654..ed83c1b70 100644 --- a/router/state/manager.go +++ b/router/state/manager.go @@ -471,16 +471,22 @@ func (a *ApiSession) SelectCtrlCh(ctrls env.NetworkControllers) channel.Channel return ctrls.AnyCtrlChannel() } -func NewApiSessionFromToken(jwtToken *jwt.Token, accessClaims *common.AccessClaims) *ApiSession { +func NewApiSessionFromToken(jwtToken *jwt.Token, accessClaims *common.AccessClaims) (*ApiSession, error) { + subj, err := jwtToken.Claims.GetSubject() + if err != nil { + return nil, fmt.Errorf("unable to get the api session identity from the JWT subject (%w)", err) + } + jwtToken.Claims.(*common.AccessClaims).Subject = subj return &ApiSession{ ApiSession: &edge_ctrl_pb.ApiSession{ Token: jwtToken.Raw, CertFingerprints: accessClaims.CertFingerprints, Id: accessClaims.JWTID, + IdentityId: subj, }, JwtToken: jwtToken, Claims: accessClaims, - } + }, nil } func (sm *ManagerImpl) GetApiSession(token string) *ApiSession { @@ -498,7 +504,12 @@ func (sm *ManagerImpl) GetApiSession(token string) *ApiSession { return nil } - return NewApiSessionFromToken(jwtToken, accessClaims) + if apiSession, err := NewApiSessionFromToken(jwtToken, accessClaims); err != nil { + pfxlog.Logger().WithError(err).Error("failed to create api session from JWT") + return nil + } else { + return apiSession + } } else { pfxlog.Logger().WithError(err).Error("JWT validation failed") return nil diff --git a/router/xgress_edge/accept.go b/router/xgress_edge/accept.go index 02087b2d4..1b1a1456f 100644 --- a/router/xgress_edge/accept.go +++ b/router/xgress_edge/accept.go @@ -21,16 +21,23 @@ import ( "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" "github.com/openziti/channel/v3/latency" + "github.com/openziti/metrics" "github.com/openziti/sdk-golang/ziti/edge" "github.com/openziti/ziti/common/cert" "math" + "sync/atomic" ) type Acceptor struct { - uListener channel.UnderlayListener - listener *listener - options *channel.Options - sessionBindHandler *sessionConnectionHandler + uListener channel.UnderlayListener + listener *listener + options *channel.Options + sessionBindHandler *sessionConnectionHandler + connectFailureMeter metrics.Meter + connectSuccessMeter metrics.Meter + disconnectMeter metrics.Meter + connectionCount atomic.Int64 + connStateTracker *connectionTracker } func (self *Acceptor) BindChannel(binding channel.Binding) error { @@ -39,7 +46,7 @@ func (self *Acceptor) BindChannel(binding channel.Binding) error { fpg := cert.NewFingerprintGenerator() - proxy := &edgeClientConn{ + conn := &edgeClientConn{ msgMux: edge.NewCowMapMsgMux(), listener: self.listener, fingerprints: fpg.FromCerts(binding.GetChannel().Certificates()), @@ -47,62 +54,81 @@ func (self *Acceptor) BindChannel(binding channel.Binding) error { idSeq: math.MaxUint32 / 2, } - log.Debug("peer fingerprints ", proxy.fingerprints) + log.Debug("peer fingerprints ", conn.fingerprints) binding.AddTypedReceiveHandler(&channel.AsyncFunctionReceiveAdapter{ Type: edge.ContentTypeConnect, Handler: func(m *channel.Message, ch channel.Channel) { - proxy.processConnect(self.listener.factory.stateManager, m, ch) + conn.processConnect(self.listener.factory.stateManager, m, ch) }, }) binding.AddTypedReceiveHandler(&channel.AsyncFunctionReceiveAdapter{ Type: edge.ContentTypeBind, Handler: func(m *channel.Message, ch channel.Channel) { - proxy.processBind(self.listener.factory.stateManager, m, ch) + conn.processBind(self.listener.factory.stateManager, m, ch) }, }) binding.AddTypedReceiveHandler(&channel.AsyncFunctionReceiveAdapter{ Type: edge.ContentTypeUnbind, Handler: func(m *channel.Message, ch channel.Channel) { - proxy.processUnbind(self.listener.factory.stateManager, m, ch) + conn.processUnbind(self.listener.factory.stateManager, m, ch) }, }) binding.AddTypedReceiveHandler(&channel.AsyncFunctionReceiveAdapter{ Type: edge.ContentTypeUpdateBind, Handler: func(m *channel.Message, ch channel.Channel) { - proxy.processUpdateBind(self.listener.factory.stateManager, m, ch) + conn.processUpdateBind(self.listener.factory.stateManager, m, ch) }, }) binding.AddTypedReceiveHandler(&channel.AsyncFunctionReceiveAdapter{ Type: edge.ContentTypeHealthEvent, Handler: func(m *channel.Message, ch channel.Channel) { - proxy.processHealthEvent(self.listener.factory.stateManager, m, ch) + conn.processHealthEvent(self.listener.factory.stateManager, m, ch) }, }) binding.AddTypedReceiveHandler(&channel.AsyncFunctionReceiveAdapter{ Type: edge.ContentTypeUpdateToken, Handler: func(m *channel.Message, ch channel.Channel) { - proxy.processTokenUpdate(self.listener.factory.stateManager, m, ch) + conn.processTokenUpdate(self.listener.factory.stateManager, m, ch) }, }) - binding.AddReceiveHandlerF(edge.ContentTypeStateClosed, proxy.msgMux.HandleReceive) + binding.AddReceiveHandlerF(edge.ContentTypeStateClosed, conn.msgMux.HandleReceive) - binding.AddReceiveHandlerF(edge.ContentTypeTraceRoute, proxy.processTraceRoute) + binding.AddReceiveHandlerF(edge.ContentTypeTraceRoute, conn.processTraceRoute) - binding.AddReceiveHandlerF(edge.ContentTypeTraceRouteResponse, proxy.msgMux.HandleReceive) + binding.AddReceiveHandlerF(edge.ContentTypeTraceRouteResponse, conn.msgMux.HandleReceive) binding.AddTypedReceiveHandler(&latency.LatencyHandler{}) - // Since data is most common type, it gets to dispatch directly - binding.AddTypedReceiveHandler(proxy.msgMux) - binding.AddCloseHandler(proxy) + // Since data is the most common type, it gets to dispatch directly + binding.AddTypedReceiveHandler(conn.msgMux) + binding.AddCloseHandler(conn) binding.AddPeekHandler(debugPeekHandler{}) - return self.sessionBindHandler.BindChannel(binding, proxy) + + if err := self.sessionBindHandler.validateApiSession(binding, conn); err != nil { + self.connectFailureMeter.Mark(1) + return err + } + + identityId := conn.apiSession.ApiSession.IdentityId + self.connStateTracker.markConnected(identityId, conn.ch) + + binding.AddCloseHandler(channel.CloseHandlerF(func(ch channel.Channel) { + self.connectionCount.Add(-1) + self.disconnectMeter.Mark(1) + self.connStateTracker.markDisconnected(identityId, ch) + })) + + self.connectSuccessMeter.Mark(1) + self.connectionCount.Add(1) + + self.sessionBindHandler.completeBinding(binding, conn) + return nil } type debugPeekHandler struct{} @@ -146,12 +172,22 @@ func NewAcceptor(listener *listener, uListener channel.UnderlayListener, options optionsWithBind = channel.DefaultOptions() } - return &Acceptor{ - listener: listener, - uListener: uListener, - options: optionsWithBind, - sessionBindHandler: sessionHandler, + result := &Acceptor{ + listener: listener, + uListener: uListener, + options: optionsWithBind, + sessionBindHandler: sessionHandler, + connStateTracker: listener.factory.connectionTracker, + connectFailureMeter: listener.factory.metricsRegistry.Meter("edge.connect.failures"), + connectSuccessMeter: listener.factory.metricsRegistry.Meter("edge.connect.successes"), + disconnectMeter: listener.factory.metricsRegistry.Meter("edge.disconnects"), } + + listener.factory.metricsRegistry.FuncGauge("edge.connections", func() int64 { + return result.connectionCount.Load() + }) + + return result } func (self *Acceptor) Run() { diff --git a/router/xgress_edge/connections.go b/router/xgress_edge/connections.go index 015f4b547..1c0f87102 100644 --- a/router/xgress_edge/connections.go +++ b/router/xgress_edge/connections.go @@ -22,14 +22,375 @@ import ( "fmt" "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" + "github.com/openziti/channel/v3/protobufs" "github.com/openziti/metrics" "github.com/openziti/sdk-golang/ziti/edge" "github.com/openziti/ziti/common/cert" + "github.com/openziti/ziti/common/inspect" + "github.com/openziti/ziti/common/pb/edge_ctrl_pb" "github.com/openziti/ziti/common/spiffehlp" + "github.com/openziti/ziti/router/env" "github.com/openziti/ziti/router/state" + cmap "github.com/orcaman/concurrent-map/v2" + "slices" "strings" + "sync" + "sync/atomic" + "time" ) +type identityConnect struct { + srcAddr string + dstAddr string + connectTime int64 +} + +type reportData struct { + connects []identityConnect + stateChanged bool +} + +func (self *reportData) hasReportData() bool { + return len(self.connects) > 0 || self.stateChanged +} + +type identityState struct { + sync.Mutex + unreported reportData + beingReported reportData + connections []channel.Channel +} + +func (self *identityState) markConnect(ch channel.Channel, queueEvent bool) { + self.Lock() + defer self.Unlock() + if queueEvent { + srcAddr := ch.Underlay().GetRemoteAddr().String() + dstAddr := ch.Underlay().GetLocalAddr().String() + + self.unreported.connects = append(self.unreported.connects, identityConnect{srcAddr: srcAddr, dstAddr: dstAddr, connectTime: time.Now().UnixMilli()}) + } + self.connections = append(self.connections, ch) + if len(self.connections) == 1 { + self.unreported.stateChanged = true + } +} + +func (self *identityState) markDisconnect(ch channel.Channel) { + self.Lock() + defer self.Unlock() + startLen := len(self.connections) + self.connections = slices.DeleteFunc(self.connections, func(elem channel.Channel) bool { + return elem == ch + }) + if startLen > 0 && len(self.connections) == 0 { + self.unreported.stateChanged = true + } +} + +func (self *identityState) getConnectedStateEvent(id string) *edge_ctrl_pb.ConnectEvents_IdentityConnectEvents { + self.Lock() + defer self.Unlock() + return &edge_ctrl_pb.ConnectEvents_IdentityConnectEvents{ + IdentityId: id, + IsConnected: len(self.connections) > 0, + } +} + +func (self *identityState) getStateEvent(id string, fullSync bool) (*edge_ctrl_pb.ConnectEvents_IdentityConnectEvents, bool) { + self.Lock() + defer self.Unlock() + + isConnected := len(self.connections) > 0 + + if !self.unreported.hasReportData() && !self.beingReported.hasReportData() { + if fullSync { + return &edge_ctrl_pb.ConnectEvents_IdentityConnectEvents{ + IdentityId: id, + IsConnected: isConnected, + }, isConnected + } + return nil, isConnected + } + + result := &edge_ctrl_pb.ConnectEvents_IdentityConnectEvents{ + IdentityId: id, + IsConnected: isConnected, + } + + for _, t := range self.beingReported.connects { + result.ConnectTimes = append(result.ConnectTimes, &edge_ctrl_pb.ConnectEvents_ConnectDetails{ + ConnectTime: t.connectTime, + SrcAddr: t.srcAddr, + DstAddr: t.dstAddr, + }) + } + + for _, t := range self.unreported.connects { + result.ConnectTimes = append(result.ConnectTimes, &edge_ctrl_pb.ConnectEvents_ConnectDetails{ + ConnectTime: t.connectTime, + SrcAddr: t.srcAddr, + DstAddr: t.dstAddr, + }) + } + + self.beingReported.connects = append(self.beingReported.connects, self.unreported.connects...) + self.beingReported.stateChanged = self.beingReported.stateChanged || self.unreported.stateChanged + self.unreported.connects = nil + self.unreported.stateChanged = false + + return result, result.IsConnected +} + +func (self *identityState) clearReported() int { + self.Lock() + defer self.Unlock() + count := len(self.beingReported.connects) + self.beingReported.connects = nil + self.beingReported.stateChanged = false + return count +} + +type connectionTracker struct { + enabled bool + lock sync.Mutex + controllers env.NetworkControllers + states cmap.ConcurrentMap[string, *identityState] + needsFullSync map[string]channel.Channel + notifyFullSync chan struct{} + batchInterval time.Duration + fullSyncInterval time.Duration + maxQueuedEvents int64 + lastFullSync time.Time + queuedEventCounter atomic.Int64 +} + +func newConnectionTracker(env env.RouterEnv) *connectionTracker { + result := &connectionTracker{ + enabled: env.GetConnectEventsConfig().Enabled, + controllers: env.GetNetworkControllers(), + states: cmap.New[*identityState](), + needsFullSync: map[string]channel.Channel{}, + notifyFullSync: make(chan struct{}, 1), + batchInterval: env.GetConnectEventsConfig().BatchInterval, + fullSyncInterval: env.GetConnectEventsConfig().FullSyncInterval, + maxQueuedEvents: env.GetConnectEventsConfig().MaxQueuedEvents, + } + + go result.runLoop(env.GetCloseNotify()) + + return result +} + +func (self *connectionTracker) runLoop(closeNotify <-chan struct{}) { + ticker := time.NewTicker(self.batchInterval) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + self.report() + self.sendFullSync() + case <-self.notifyFullSync: + self.sendFullSync() + case <-closeNotify: + return + } + } +} + +func (self *connectionTracker) notifyNeedsFullSync() { + select { + case self.notifyFullSync <- struct{}{}: + default: + } +} + +func (self *connectionTracker) markConnected(identityId string, ch channel.Channel) { + pfxlog.Logger().WithField("identityId", identityId).Trace("marking connected") + queueEvent := self.enabled && self.queuedEventCounter.Load() < self.maxQueuedEvents + self.states.Upsert(identityId, nil, func(exist bool, valueInMap *identityState, newValue *identityState) *identityState { + if valueInMap == nil { + valueInMap = &identityState{} + } + valueInMap.markConnect(ch, queueEvent) + return valueInMap + }) + + if queueEvent { + self.queuedEventCounter.Add(1) + } +} + +func (self *connectionTracker) markDisconnected(identityId string, ch channel.Channel) { + pfxlog.Logger().WithField("identityId", identityId).Trace("marking disconnected") + self.states.Upsert(identityId, nil, func(exist bool, valueInMap *identityState, newValue *identityState) *identityState { + if valueInMap == nil { + valueInMap = &identityState{} + } + valueInMap.markDisconnect(ch) + return valueInMap + }) +} + +func (self *connectionTracker) report() { + self.lock.Lock() + defer self.lock.Unlock() + + startTime := time.Now() + fullSync := time.Since(self.lastFullSync) > self.fullSyncInterval + + var removeCheck []string + evts := &edge_ctrl_pb.ConnectEvents{ + FullState: fullSync, + } + + self.states.IterCb(func(key string, v *identityState) { + evt, connected := v.getStateEvent(key, fullSync) + if !connected && evt == nil { + removeCheck = append(removeCheck, key) + } + if evt != nil { + evts.Events = append(evts.Events, evt) + } + }) + + for _, k := range removeCheck { + self.states.RemoveCb(k, func(key string, v *identityState, exists bool) bool { + if v == nil { + return true + } + + v.Lock() + defer v.Unlock() + return len(v.connections) == 0 && !v.unreported.hasReportData() && !v.beingReported.hasReportData() + }) + } + + if len(evts.Events) > 0 || evts.FullState { + if self.sendEvents(evts) { + if fullSync { + self.lastFullSync = startTime + } + + self.states.IterCb(func(key string, v *identityState) { + clearedCount := v.clearReported() + if clearedCount > 0 { + self.queuedEventCounter.Add(int64(-clearedCount)) + } + }) + } + } else if fullSync { + self.lastFullSync = startTime + } +} + +func (self *connectionTracker) sendEvents(evts *edge_ctrl_pb.ConnectEvents) bool { + successfulSend := false + self.controllers.ForEach(func(ctrlId string, ch channel.Channel) { + pfxlog.Logger().WithField("ctrlId", ch.Id()).WithField("fullSync", evts.FullState).Trace("sending connect events") + + if err := protobufs.MarshalTyped(evts).WithTimeout(time.Second).Send(ch); err != nil { + pfxlog.Logger().WithField("ctrlId", ctrlId).WithError(err).Error("error sending connect events") + self.needsFullSync[ctrlId] = ch + self.notifyNeedsFullSync() + } else { + successfulSend = true + if evts.FullState { + delete(self.needsFullSync, ctrlId) + } + } + }) + return successfulSend +} + +func (self *connectionTracker) sendFullSync() { + self.lock.Lock() + defer self.lock.Unlock() + + ctrls := map[string]channel.Channel{} + for k := range self.needsFullSync { + ctrl := self.controllers.GetNetworkController(k) + if ctrl == nil { + delete(self.needsFullSync, k) + } else if ctrl.IsConnected() { + ctrls[k] = ctrl.Channel() + } + } + + if len(ctrls) == 0 { + return + } + + evts := &edge_ctrl_pb.ConnectEvents{ + FullState: true, + } + + self.states.IterCb(func(key string, v *identityState) { + evt := v.getConnectedStateEvent(key) + if evt.IsConnected { + evts.Events = append(evts.Events, evt) + } + }) + + for ctrlId, ch := range ctrls { + pfxlog.Logger().WithField("ctrlId", ch.Id()).Trace("doing full connection state sync") + if err := protobufs.MarshalTyped(evts).WithTimeout(time.Second).Send(ch); err != nil { + pfxlog.Logger().WithField("ctrlId", ctrlId).WithError(err).Error("error sending connect events") + } else { + delete(self.needsFullSync, ctrlId) + } + } +} + +func (self *connectionTracker) NotifyOfReconnect(ch channel.Channel) { + self.lock.Lock() + defer self.lock.Unlock() + + pfxlog.Logger().WithField("ctrlId", ch.Id()).Debug("sending full sync of connection state after reconnect") + self.needsFullSync[ch.Id()] = ch + self.notifyNeedsFullSync() +} + +func (self *connectionTracker) Inspect(_ string, _ time.Duration) any { + self.lock.Lock() + result := &inspect.RouterIdentityConnections{ + IdentityConnections: map[string]*inspect.RouterIdentityConnectionDetail{}, + LastFullSync: self.lastFullSync.Format(time.RFC3339), + QueuedEventCount: self.queuedEventCounter.Load(), + MaxQueuedEvents: self.maxQueuedEvents, + BatchInterval: self.batchInterval.String(), + FullSyncInterval: self.fullSyncInterval.String(), + } + for ctrlId := range self.needsFullSync { + result.NeedFullSync = append(result.NeedFullSync, ctrlId) + } + self.lock.Unlock() + + for entry := range self.states.IterBuffered() { + identityId := entry.Key + states := entry.Val + states.Lock() + identityDetail := &inspect.RouterIdentityConnectionDetail{ + UnreportedCount: uint64(len(states.unreported.connects)), + UnreportedStateChanged: states.unreported.stateChanged, + BeingReportedCount: uint64(len(states.beingReported.connects)), + BeingReportedStateChanged: states.beingReported.stateChanged, + } + for _, ch := range states.connections { + identityDetail.Connections = append(identityDetail.Connections, &inspect.RouterConnectionDetail{ + Id: ch.Id(), + Closed: ch.IsClosed(), + SrcAddr: ch.Underlay().GetRemoteAddr().String(), + DstAddr: ch.Underlay().GetLocalAddr().String(), + }) + } + entry.Val.Unlock() + result.IdentityConnections[identityId] = identityDetail + } + return result +} + type sessionConnectionHandler struct { stateManager state.Manager options *Options @@ -46,7 +407,7 @@ func newSessionConnectHandler(stateManager state.Manager, options *Options, metr } } -func (handler *sessionConnectionHandler) BindChannel(binding channel.Binding, edgeConn *edgeClientConn) error { +func (handler *sessionConnectionHandler) validateApiSession(binding channel.Binding, edgeConn *edgeClientConn) error { ch := binding.GetChannel() binding.AddCloseHandler(handler) @@ -96,23 +457,6 @@ func (handler *sessionConnectionHandler) BindChannel(binding channel.Binding, ed } if isValid { - if apiSession.Claims != nil { - token = apiSession.Claims.ApiSessionId - } - - removeListener := handler.stateManager.AddApiSessionRemovedListener(token, func(token string) { - if !ch.IsClosed() { - if err := ch.Close(); err != nil { - pfxlog.Logger().WithError(err).Error("could not close channel during api session removal") - } - } - - handler.stateManager.RemoveActiveChannel(ch) - }) - - handler.stateManager.AddActiveChannel(ch, apiSession) - handler.stateManager.AddConnectedApiSessionWithChannel(token, removeListener, ch) - return nil } @@ -120,6 +464,29 @@ func (handler *sessionConnectionHandler) BindChannel(binding channel.Binding, ed return errors.New("invalid client certificate for api session") } +func (handler *sessionConnectionHandler) completeBinding(binding channel.Binding, edgeConn *edgeClientConn) { + ch := binding.GetChannel() + apiSession := edgeConn.apiSession + byteToken := ch.Underlay().Headers()[edge.SessionTokenHeader] + token := string(byteToken) + if apiSession.Claims != nil { + token = apiSession.Claims.ApiSessionId + } + + removeListener := handler.stateManager.AddApiSessionRemovedListener(token, func(token string) { + if !ch.IsClosed() { + if err := ch.Close(); err != nil { + pfxlog.Logger().WithError(err).Error("could not close channel during api session removal") + } + } + + handler.stateManager.RemoveActiveChannel(ch) + }) + + handler.stateManager.AddActiveChannel(ch, apiSession) + handler.stateManager.AddConnectedApiSessionWithChannel(token, removeListener, ch) +} + func (handler *sessionConnectionHandler) validateByFingerprint(apiSession *state.ApiSession, clientFingerprint string) bool { for _, fingerprint := range apiSession.CertFingerprints { if clientFingerprint == fingerprint { diff --git a/router/xgress_edge/factory.go b/router/xgress_edge/factory.go index 3dca81160..000085378 100644 --- a/router/xgress_edge/factory.go +++ b/router/xgress_edge/factory.go @@ -20,11 +20,13 @@ import ( "fmt" "github.com/michaelquigley/pfxlog" "github.com/openziti/channel/v3" + "github.com/openziti/foundation/v2/concurrenz" "github.com/openziti/foundation/v2/versions" "github.com/openziti/metrics" "github.com/openziti/sdk-golang/ziti/edge" "github.com/openziti/transport/v2" "github.com/openziti/ziti/common" + "github.com/openziti/ziti/common/inspect" "github.com/openziti/ziti/common/pb/edge_ctrl_pb" "github.com/openziti/ziti/router" "github.com/openziti/ziti/router/env" @@ -38,17 +40,30 @@ import ( "time" ) +type reconnectionHandler interface { + NotifyOfReconnect(ch channel.Channel) +} + type Factory struct { - ctrls env.NetworkControllers - enabled bool - routerConfig *router.Config - edgeRouterConfig *edgerouter.Config - hostedServices *hostedServiceRegistry - stateManager state.Manager - versionProvider versions.VersionProvider - certChecker *CertExpirationChecker - metricsRegistry metrics.Registry - env env.RouterEnv + ctrls env.NetworkControllers + enabled bool + routerConfig *router.Config + edgeRouterConfig *edgerouter.Config + hostedServices *hostedServiceRegistry + stateManager state.Manager + versionProvider versions.VersionProvider + certChecker *CertExpirationChecker + metricsRegistry metrics.Registry + env env.RouterEnv + reconnectionHandlers concurrenz.CopyOnWriteSlice[reconnectionHandler] + connectionTracker *connectionTracker +} + +func (factory *Factory) Inspect(key string, timeout time.Duration) any { + if key == inspect.RouterIdentityConnectionStatusesKey { + return factory.connectionTracker.Inspect(key, timeout) + } + return nil } func (factory *Factory) GetNetworkControllers() env.NetworkControllers { @@ -78,6 +93,14 @@ func (factory *Factory) NotifyOfReconnect(ch channel.Channel) { factory.hostedServices.HandleReconnect() go factory.stateManager.ValidateSessions(ch, factory.edgeRouterConfig.SessionValidateChunkSize, factory.edgeRouterConfig.SessionValidateMinInterval, factory.edgeRouterConfig.SessionValidateMaxInterval) + + for _, handler := range factory.reconnectionHandlers.Value() { + go handler.NotifyOfReconnect(ch) + } +} + +func (factory *Factory) addReconnectionHandler(h reconnectionHandler) { + factory.reconnectionHandlers.Append(h) } func (factory *Factory) GetTraceDecoders() []channel.TraceMessageDecoder { @@ -133,14 +156,16 @@ func (factory *Factory) LoadConfig(configMap map[interface{}]interface{}) error // NewFactory constructs a new Edge Xgress Factory instance func NewFactory(routerConfig *router.Config, env env.RouterEnv, stateManager state.Manager) *Factory { factory := &Factory{ - ctrls: env.GetNetworkControllers(), - hostedServices: newHostedServicesRegistry(env, stateManager), - stateManager: stateManager, - versionProvider: env.GetVersionInfo(), - routerConfig: routerConfig, - metricsRegistry: env.GetMetricsRegistry(), - env: env, + ctrls: env.GetNetworkControllers(), + hostedServices: newHostedServicesRegistry(env, stateManager), + stateManager: stateManager, + versionProvider: env.GetVersionInfo(), + routerConfig: routerConfig, + metricsRegistry: env.GetMetricsRegistry(), + env: env, + connectionTracker: newConnectionTracker(env), } + factory.addReconnectionHandler(factory.connectionTracker) return factory } @@ -183,7 +208,9 @@ func (factory *Factory) CreateDialer(optionsData xgress.OptionsData) (xgress.Dia return nil, err } - pfxlog.Logger().Infof("xgress edge dialer options: %v", options.ToLoggableString()) + // CreateDialer is called for every egress route and for inspect and validations + // can't log this every time. + // pfxlog.Logger().Infof("xgress edge dialer options: %v", options.ToLoggableString()) return newDialer(factory, options), nil } @@ -267,5 +294,6 @@ func (options *Options) load(data xgress.OptionsData) error { } else { options.channelOptions = channel.DefaultOptions() } + return nil } diff --git a/router/xgress_edge/listener.go b/router/xgress_edge/listener.go index 6ba63ea6a..3e81baee8 100644 --- a/router/xgress_edge/listener.go +++ b/router/xgress_edge/listener.go @@ -770,7 +770,16 @@ func (self *edgeClientConn) processTokenUpdate(manager state.Manager, req *chann return } - newApiSession := state.NewApiSessionFromToken(newToken, newClaims) + newApiSession, err := state.NewApiSessionFromToken(newToken, newClaims) + if err != nil { + reply := edge.NewUpdateTokenFailedMsg(errors.Wrap(err, "failed to update a JWT based api session")) + reply.ReplyTo(req) + + if err := ch.Send(reply); err != nil { + logrus.WithError(err).WithField("reqSeq", reply.Sequence()).Error("error responding to token update request with update failure") + } + return + } if err := self.listener.factory.stateManager.UpdateChApiSession(ch, newApiSession); err != nil { reply := edge.NewUpdateTokenFailedMsg(errors.Wrap(err, "failed to update a JWT based api session")) diff --git a/version b/version index 9459d4ba2..5625e59da 100644 --- a/version +++ b/version @@ -1 +1 @@ -1.1 +1.2 diff --git a/ziti/cmd/agentcli/agent_cluster_add.go b/ziti/cmd/agentcli/agent_cluster_add.go index 67eca327c..3ad9d27a5 100644 --- a/ziti/cmd/agentcli/agent_cluster_add.go +++ b/ziti/cmd/agentcli/agent_cluster_add.go @@ -22,7 +22,6 @@ import ( "github.com/openziti/ziti/common/pb/mgmt_pb" "github.com/openziti/ziti/controller" "github.com/openziti/ziti/ziti/cmd/common" - cmdhelper "github.com/openziti/ziti/ziti/cmd/helpers" "github.com/spf13/cobra" ) @@ -43,11 +42,10 @@ func NewAgentClusterAdd(p common.OptionsProvider) *cobra.Command { Args: cobra.ExactArgs(1), Use: "add ", Short: "adds a node to the controller cluster", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { action.Cmd = cmd action.Args = args - err := action.MakeChannelRequest(byte(AgentAppController), action.makeRequest) - cmdhelper.CheckErr(err) + return action.MakeChannelRequest(byte(AgentAppController), action.makeRequest) }, } @@ -72,10 +70,9 @@ func (self *AgentClusterAddAction) makeRequest(ch channel.Channel) error { return err } result := channel.UnmarshalResult(reply) - if result.Success { - fmt.Println(result.Message) - } else { - fmt.Printf("error: %v\n", result.Message) + if !result.Success { + return fmt.Errorf("cluster add failed: %s", result.Message) } + fmt.Println(result.Message) return nil } diff --git a/ziti/cmd/fabric/inspect.go b/ziti/cmd/fabric/inspect.go index 6e2c65b5d..72961ccf1 100644 --- a/ziti/cmd/fabric/inspect.go +++ b/ziti/cmd/fabric/inspect.go @@ -7,6 +7,7 @@ import ( "github.com/fatih/color" "github.com/openziti/foundation/v2/errorz" "github.com/openziti/foundation/v2/stringz" + inspectCommon "github.com/openziti/ziti/common/inspect" "github.com/openziti/ziti/controller/rest_client/inspect" "github.com/openziti/ziti/controller/rest_model" "github.com/openziti/ziti/ziti/cmd/api" @@ -36,6 +37,7 @@ func newInspectCmd(p common.OptionsProvider) *cobra.Command { cmd.AddCommand(action.newInspectSubCmd(p, "router-data-model", "gets information about the router data model")) cmd.AddCommand(action.newInspectSubCmd(p, "router-controllers", "gets information about the state of a router's connections to its controllers")) cmd.AddCommand(action.newInspectSubCmd(p, "terminator-costs", "gets information about terminator dynamic costs")) + cmd.AddCommand(action.newInspectSubCmd(p, inspectCommon.RouterIdentityConnectionStatusesKey, "gets information about controller identity state")) inspectCircuitsAction := &InspectCircuitsAction{InspectAction: *newInspectAction(p)} cmd.AddCommand(inspectCircuitsAction.newCobraCmd()) diff --git a/ziti/cmd/fabric/root.go b/ziti/cmd/fabric/root.go index f71786e08..fd44e4523 100644 --- a/ziti/cmd/fabric/root.go +++ b/ziti/cmd/fabric/root.go @@ -112,6 +112,7 @@ func newValidateCommand(p common.OptionsProvider) *cobra.Command { validateCmd.AddCommand(NewValidateRouterLinksCmd(p)) validateCmd.AddCommand(NewValidateRouterSdkTerminatorsCmd(p)) validateCmd.AddCommand(NewValidateRouterDataModelCmd(p)) + validateCmd.AddCommand(NewValidateIdentityConnectionStatusesCmd(p)) return validateCmd } diff --git a/ziti/cmd/fabric/validate_identity_connection_statuses.go b/ziti/cmd/fabric/validate_identity_connection_statuses.go new file mode 100644 index 000000000..54be4f874 --- /dev/null +++ b/ziti/cmd/fabric/validate_identity_connection_statuses.go @@ -0,0 +1,148 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package fabric + +import ( + "fmt" + "github.com/michaelquigley/pfxlog" + "github.com/openziti/channel/v3" + "github.com/openziti/channel/v3/protobufs" + "github.com/openziti/ziti/common/pb/mgmt_pb" + "github.com/openziti/ziti/ziti/cmd/api" + "github.com/openziti/ziti/ziti/cmd/common" + "github.com/spf13/cobra" + "google.golang.org/protobuf/proto" + "os" + "time" +) + +type validateIdentityConnectionStatusesAction struct { + api.Options + includeValidRouters bool + + eventNotify chan *mgmt_pb.RouterIdentityConnectionStatusesDetails +} + +func NewValidateIdentityConnectionStatusesCmd(p common.OptionsProvider) *cobra.Command { + action := validateIdentityConnectionStatusesAction{ + Options: api.Options{ + CommonOptions: p(), + }, + } + + validateLinksCmd := &cobra.Command{ + Use: "identity-connection-statuses ", + Short: "Validate identity connection statuses", + Example: "ziti fabric validate router-data-model --filter 'name=\"my-router\"' --include-valid-routers", + Args: cobra.MaximumNArgs(1), + RunE: action.validateRouterDataModel, + } + + action.AddCommonFlags(validateLinksCmd) + validateLinksCmd.Flags().BoolVar(&action.includeValidRouters, "include-successes", false, "Don't hide results for successes") + return validateLinksCmd +} + +func (self *validateIdentityConnectionStatusesAction) validateRouterDataModel(_ *cobra.Command, args []string) error { + closeNotify := make(chan struct{}) + self.eventNotify = make(chan *mgmt_pb.RouterIdentityConnectionStatusesDetails, 1) + + bindHandler := func(binding channel.Binding) error { + binding.AddReceiveHandler(int32(mgmt_pb.ContentType_ValidateIdentityConnectionStatusesResultType), self) + binding.AddCloseHandler(channel.CloseHandlerF(func(ch channel.Channel) { + close(closeNotify) + })) + return nil + } + + ch, err := api.NewWsMgmtChannel(channel.BindHandlerF(bindHandler)) + if err != nil { + return err + } + + filter := "" + if len(args) > 0 { + filter = args[0] + } + + request := &mgmt_pb.ValidateIdentityConnectionStatusesRequest{ + RouterFilter: filter, + } + + responseMsg, err := protobufs.MarshalTyped(request).WithTimeout(time.Duration(self.Timeout) * time.Second).SendForReply(ch) + + response := &mgmt_pb.ValidateIdentityConnectionStatusesResponse{} + if err = protobufs.TypedResponse(response).Unmarshall(responseMsg, err); err != nil { + return err + } + + if !response.Success { + return fmt.Errorf("failed to start identity connection status validation: %s", response.Message) + } + + fmt.Printf("started validation of %v components\n", response.ComponentCount) + + expected := response.ComponentCount + + errCount := 0 + for expected > 0 { + select { + case <-closeNotify: + fmt.Printf("channel closed, exiting") + return nil + case detail := <-self.eventNotify: + headerDone := false + outputHeader := func() { + if !headerDone { + if detail.ComponentType == "controller" { + fmt.Printf("controllerId: %s, success: %v\n", detail.ComponentId, detail.ValidateSuccess) + } else if detail.ComponentType == "router" { + fmt.Printf("routerId: %s, routerName: %v, success: %v\n", + detail.ComponentId, detail.ComponentName, detail.ValidateSuccess) + } + } + headerDone = true + } + + if self.includeValidRouters { + outputHeader() + } + + for _, errDetail := range detail.Errors { + outputHeader() + fmt.Printf("\t%s\n", errDetail) + errCount++ + } + expected-- + } + } + fmt.Printf("%v errors found\n", errCount) + if errCount > 0 { + os.Exit(1) + } + return nil +} + +func (self *validateIdentityConnectionStatusesAction) HandleReceive(msg *channel.Message, _ channel.Channel) { + detail := &mgmt_pb.RouterIdentityConnectionStatusesDetails{} + if err := proto.Unmarshal(msg.Body, detail); err != nil { + pfxlog.Logger().WithError(err).Error("unable to unmarshal router data model details") + return + } + + self.eventNotify <- detail +} diff --git a/zititest/go.mod b/zititest/go.mod index 81c1e4066..6b96e7165 100644 --- a/zititest/go.mod +++ b/zititest/go.mod @@ -11,14 +11,14 @@ require ( github.com/google/uuid v1.6.0 github.com/michaelquigley/pfxlog v0.6.10 github.com/openziti/agent v1.0.18 - github.com/openziti/channel/v3 v3.0.6 - github.com/openziti/edge-api v0.26.34 - github.com/openziti/fablab v0.5.60 + github.com/openziti/channel/v3 v3.0.7 + github.com/openziti/edge-api v0.26.35 + github.com/openziti/fablab v0.5.72 github.com/openziti/foundation/v2 v2.0.49 - github.com/openziti/identity v1.0.86 + github.com/openziti/identity v1.0.87 github.com/openziti/sdk-golang v0.23.44 github.com/openziti/storage v0.3.2 - github.com/openziti/transport/v2 v2.0.147 + github.com/openziti/transport/v2 v2.0.148 github.com/openziti/ziti v0.28.3 github.com/orcaman/concurrent-map/v2 v2.0.1 github.com/pkg/errors v0.9.1 @@ -43,7 +43,7 @@ require ( github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go v1.52.6 // indirect + github.com/aws/aws-sdk-go v1.55.5 // indirect github.com/biogo/store v0.0.0-20200525035639-8c94ae1e7c9c // indirect github.com/blang/semver v3.5.1+incompatible // indirect github.com/boltdb/bolt v1.3.1 // indirect @@ -100,7 +100,7 @@ require ( github.com/hashicorp/raft-boltdb v0.0.0-20220329195025-15018e9b97e0 // indirect github.com/iancoleman/strcase v0.1.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/influxdata/influxdb-client-go/v2 v2.13.0 // indirect + github.com/influxdata/influxdb-client-go/v2 v2.14.0 // indirect github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/jedib0t/go-pretty/v6 v6.5.9 // indirect diff --git a/zititest/go.sum b/zititest/go.sum index 6b456384a..c018fab53 100644 --- a/zititest/go.sum +++ b/zititest/go.sum @@ -95,8 +95,8 @@ github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.52.6 h1:nw1AMg0wIj5tTnI89KaDe9G5aISqXm4KJEe1DfNbFvA= -github.com/aws/aws-sdk-go v1.52.6/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -408,8 +408,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/influxdata/influxdb-client-go/v2 v2.2.2/go.mod h1:fa/d1lAdUHxuc1jedx30ZfNG573oQTQmUni3N6pcW+0= -github.com/influxdata/influxdb-client-go/v2 v2.13.0 h1:ioBbLmR5NMbAjP4UVA5r9b5xGjpABD7j65pI8kFphDM= -github.com/influxdata/influxdb-client-go/v2 v2.13.0/go.mod h1:k+spCbt9hcvqvUiz0sr5D8LolXHqAAOfPw9v/RIRHl4= +github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjwJdUHnwvfjMF71M1iI4= +github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d h1:/WZQPMZNsjZ7IlCpsLGdQBINg5bxKQ1K1sh6awxLtkA= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= @@ -592,20 +592,20 @@ github.com/openziti-incubator/cf v0.0.3 h1:JKs55DbaIxl87nI/Ra/3DHMiz5iaPpu8JjsuN github.com/openziti-incubator/cf v0.0.3/go.mod h1:6abCY06bCjKmK2I9kohij+cp9uXIPFiFwSCNZPdMk8E= github.com/openziti/agent v1.0.18 h1:+MP1AXGresJPcbhbsFdElpTWqrQW+VZOLya0V+/mGbE= github.com/openziti/agent v1.0.18/go.mod h1:HET46hghk8ahnVt/3mfVjmnL4NLNVZGnqvrQC3PbIn8= -github.com/openziti/channel/v3 v3.0.6 h1:HnV7im5cdOWltw5aYOoJb8TsZ+JLsDGOqwU/fhLm7e4= -github.com/openziti/channel/v3 v3.0.6/go.mod h1:YV+xmG4kptP48BAVMKJS+Pbuw0j3hWoWervh8m2aYpA= +github.com/openziti/channel/v3 v3.0.7 h1:Xi29/KszQcB0Zs466EQYEwORdw+1BBL9TWFlFh70QPw= +github.com/openziti/channel/v3 v3.0.7/go.mod h1:cpaBPj8bMLTDzuQFFxAvbEWcbzFN2XMwAqK33iRLa0M= github.com/openziti/cobra-to-md v1.0.1 h1:WRinNoIRmwWUSJm+pSNXMjOrtU48oxXDZgeCYQfVXxE= github.com/openziti/cobra-to-md v1.0.1/go.mod h1:FjCpk/yzHF7/r28oSTNr5P57yN5VolpdAtS/g7KNi2c= github.com/openziti/dilithium v0.3.5 h1:+envGNzxc3OyVPiuvtxivQmCsOjdZjtOMLpQBeMz7eM= github.com/openziti/dilithium v0.3.5/go.mod h1:XONq1iK6te/WwNzkgZHfIDHordMPqb0hMwJ8bs9EfSk= -github.com/openziti/edge-api v0.26.34 h1:73OcSpEeE2pdLKxPH2cGwDi8YcROWRGJfR+BAFRPGIE= -github.com/openziti/edge-api v0.26.34/go.mod h1:sYHVpm26Jr1u7VooNJzTb2b2nGSlmCHMnbGC8XfWSng= -github.com/openziti/fablab v0.5.60 h1:RsqrEb3LV6asK5N97uZKyNSDhcNOeDcAuT4OAD/hY9Y= -github.com/openziti/fablab v0.5.60/go.mod h1:B/ib+GOtozEIytv2aXSFl9+dL7AiGfbpGS/VjnNduU8= +github.com/openziti/edge-api v0.26.35 h1:32ILwMAPCQrf5ZVIR8IQO5AQwblIM60yD1+ABw48vXU= +github.com/openziti/edge-api v0.26.35/go.mod h1:sYHVpm26Jr1u7VooNJzTb2b2nGSlmCHMnbGC8XfWSng= +github.com/openziti/fablab v0.5.72 h1:omcV1vS8C7FS1gnEFItczq7irf/8pMnvZNg/L7M/N2Q= +github.com/openziti/fablab v0.5.72/go.mod h1:qETYWQwY7QClVPQMpB5mS7izh2Bnk3LzbhbCrprbAR0= github.com/openziti/foundation/v2 v2.0.49 h1:aQ5I/lMhkHQ6urhRpLwrWP+7YtoeUitCfY/wub+nOqo= github.com/openziti/foundation/v2 v2.0.49/go.mod h1:tFk7wg5WE/nDDur5jSVQTROugKDXQkFvmqRSV4pvWp0= -github.com/openziti/identity v1.0.86 h1:VmOdm+NCw0ZnPprR0nOMX43I2O+Q+b/FULNTnvcXXiw= -github.com/openziti/identity v1.0.86/go.mod h1:beIXWNDImEjZn93XPOorJzyuQCQUYOvKFQ0fWhLN2qM= +github.com/openziti/identity v1.0.87 h1:v0NnaDee/5GkPGSoG+2XTl0+0b3BDsm1R6EkkBmK+Zw= +github.com/openziti/identity v1.0.87/go.mod h1:beIXWNDImEjZn93XPOorJzyuQCQUYOvKFQ0fWhLN2qM= github.com/openziti/jwks v1.0.6 h1:PR+9OVaMO8oHEoVQmHqeUBExWwLWyODEGJQK2DXHaqE= github.com/openziti/jwks v1.0.6/go.mod h1:t4xxq8vlXGsPn29kiQVnZBBDDnEoOFqtJoHibkJunQQ= github.com/openziti/metrics v1.2.58 h1:AbHSTMKHP/o6r6fh7a08c486Y/5f5xjkZQbcyn3w1tM= @@ -618,8 +618,8 @@ github.com/openziti/secretstream v0.1.25 h1:40gHKcAcoXqKs0J7Tz1jTAmPoMXmMn4HP3Mg github.com/openziti/secretstream v0.1.25/go.mod h1:zgBcyN7h/zLBIWeqSrWwlOGOMQW51oQGYYlkiArR6Ec= github.com/openziti/storage v0.3.2 h1:etRAT2asJvV1gKgj/eRu3st7AO0TKgDagsEpDdIj/l0= github.com/openziti/storage v0.3.2/go.mod h1:yTv6Rqs8Rk6nMPUD+96VXI5eWhOARTNLV0OPmgiK8I4= -github.com/openziti/transport/v2 v2.0.147 h1:YXNIu17SKecdfxQwLDeyF2bIkE6h4XOrlyzMdVeHuJY= -github.com/openziti/transport/v2 v2.0.147/go.mod h1:ovDQhNomg+Vl8cdHCeqG7HrRIsedkfhJQ1/QAm8Ktic= +github.com/openziti/transport/v2 v2.0.148 h1:2xNPyWp3eY31NsqzldRSpVuRnLpybTU4MkNPltPg80c= +github.com/openziti/transport/v2 v2.0.148/go.mod h1:n9QrYf+nudf4aSU4hvtC5WPTxqPJT6Vpg+vOSk8ae4I= github.com/openziti/x509-claims v1.0.3 h1:HNdQ8Nf1agB3lBs1gahcO6zfkeS4S5xoQ2/PkY4HRX0= github.com/openziti/x509-claims v1.0.3/go.mod h1:Z0WIpBm6c4ecrpRKrou6Gk2wrLWxJO/+tuUwKh8VewE= github.com/openziti/xweb/v2 v2.1.3 h1:smHMs6BCdSF3LB3KMHvR8YcNYKESJjM9LBfHi958/2E= diff --git a/zititest/models/links-test/main.go b/zititest/models/links-test/main.go index 09d9207e0..3994ec90e 100644 --- a/zititest/models/links-test/main.go +++ b/zititest/models/links-test/main.go @@ -224,7 +224,7 @@ var m = &model.Model{ workflow.AddAction(component.Start(".ctrl")) workflow.AddAction(semaphore.Sleep(2 * time.Second)) - workflow.AddAction(edge.RaftJoin(".ctrl")) + workflow.AddAction(edge.RaftJoin("ctrl1", ".ctrl")) workflow.AddAction(semaphore.Sleep(2 * time.Second)) workflow.AddAction(edge.InitRaftController("#ctrl1")) workflow.AddAction(edge.ControllerAvailable("#ctrl1", 30*time.Second)) diff --git a/zititest/models/links-test/validation.go b/zititest/models/links-test/validation.go index b48dd1008..135664fa8 100644 --- a/zititest/models/links-test/validation.go +++ b/zititest/models/links-test/validation.go @@ -53,7 +53,7 @@ func validateLinks(run model.Run) error { deadline := time.Now().Add(15 * time.Minute) for _, ctrl := range ctrls { ctrlComponent := ctrl - go validateLinksForCtrlWithChan(ctrlComponent, deadline, errC) + go validateLinksForCtrlWithChan(run, ctrlComponent, deadline, errC) } for i := 0; i < len(ctrls); i++ { @@ -66,38 +66,14 @@ func validateLinks(run model.Run) error { return nil } -func validateLinksForCtrlWithChan(c *model.Component, deadline time.Time, errC chan<- error) { - errC <- validateLinksForCtrl(c, deadline) +func validateLinksForCtrlWithChan(run model.Run, c *model.Component, deadline time.Time, errC chan<- error) { + errC <- validateLinksForCtrl(run, c, deadline) } -func validateLinksForCtrl(c *model.Component, deadline time.Time) error { - username := c.MustStringVariable("credentials.edge.username") - password := c.MustStringVariable("credentials.edge.password") - edgeApiBaseUrl := c.Host.PublicIp + ":1280" - - var clients *zitirest.Clients - loginStart := time.Now() - for { - var err error - clients, err = zitirest.NewManagementClients(edgeApiBaseUrl) - if err != nil { - if time.Since(loginStart) > time.Minute { - return err - } - pfxlog.Logger().WithField("ctrlId", c.Id).WithError(err).Info("failed to initialize mgmt client, trying again in 1s") - time.Sleep(time.Second) - continue - } - - if err = clients.Authenticate(username, password); err != nil { - if time.Since(loginStart) > time.Minute { - return err - } - pfxlog.Logger().WithField("ctrlId", c.Id).WithError(err).Info("failed to authenticate, trying again in 1s") - time.Sleep(time.Second) - } else { - break - } +func validateLinksForCtrl(run model.Run, c *model.Component, deadline time.Time) error { + clients, err := chaos.EnsureLoggedIntoCtrl(run, c, time.Minute) + if err != nil { + return err } allLinksPresent := false diff --git a/zititest/models/sdk-hosting-test/main.go b/zititest/models/sdk-hosting-test/main.go index ac5b76454..d75ccd9e2 100644 --- a/zititest/models/sdk-hosting-test/main.go +++ b/zititest/models/sdk-hosting-test/main.go @@ -246,7 +246,7 @@ var m = &model.Model{ if isHA { workflow.AddAction(semaphore.Sleep(2 * time.Second)) - workflow.AddAction(edge.RaftJoin(".ctrl")) + workflow.AddAction(edge.RaftJoin("ctrl1", ".ctrl")) workflow.AddAction(semaphore.Sleep(2 * time.Second)) workflow.AddAction(edge.InitRaftController("#ctrl1")) } diff --git a/zititest/models/sdk-status-test/configs/ctrl.yml.tmpl b/zititest/models/sdk-status-test/configs/ctrl.yml.tmpl new file mode 100644 index 000000000..ee4a387b6 --- /dev/null +++ b/zititest/models/sdk-status-test/configs/ctrl.yml.tmpl @@ -0,0 +1,200 @@ +v: 3 + +raft: + dataDir: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/ctrldata + +identity: + cert: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/certs/{{ .Component.Id }}-server.chain.pem + key: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/keys/{{ .Component.Id }}-server.key + ca: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/certs/{{ .Component.Id }}.chain.pem + +trustDomain: sdk-hosting-test + +commandRateLimiter: + enabled: true + maxQueued: 25 + +# the endpoint that routers will connect to the controller over. +ctrl: + listener: tls:0.0.0.0:6262 + options: + advertiseAddress: tls:{{ .Host.PublicIp }}:6262 + # (optional) settings + # set the maximum number of connect requests that are buffered and waiting to be acknowledged (1 to 5000, default 1000) + #maxQueuedConnects: 50 + + # the maximum number of connects that have begun hello synchronization (1 to 1000, default 16) + #maxOutstandingConnects: 100 + + # the number of milliseconds to wait before a hello synchronization fails and closes the connection (30ms to 60000ms, default: 1000ms) + #connectTimeoutMs: 3000 + + # Sets the control channel write timeout. A write timeout will close the control channel, so the router will reconnect + #writeTimeout: 15s + + # A listener address which will be sent to connecting routers in order to change their configured controller + # address. If defined, routers will update address configuration to immediately use the new address for future + # connections. The value of newListener must be resolvable both via DNS and validate via certificates + #newListener: tls:localhost:6262 + +#events: +# jsonLogger: +# subscriptions: +# - type: entityChange +# - type: edge.apiSessions +# - type: edge.entityCounts +# interval: 15s +# - type: edge.sessions +# - type: fabric.routers +# - type: fabric.terminators +# - type: metrics +# sourceFilter: .* +# metricFilter: .*egress.*m1_rate* +# - type: fabric.circuits +# include: +# - created +# include: +# - created +# - type: fabric.usage +# - type: services +# - type: fabric.usage +# handler: +# type: file +# format: json +# path: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/logs/event.log + +healthChecks: + boltCheck: + # How often to try entering a bolt read tx. Defaults to 30 seconds + interval: 30s + # When to timeout the check. Defaults to 15 seconds + timeout: 15s + # How long to wait before starting the check. Defaults to 15 seconds + initialDelay: 15s + +# By having an 'edge' section defined, the ziti-controller will attempt to parse the edge configuration. Removing this +# section, commenting out, or altering the name of the section will cause the edge to not run. +edge: + identityStatusConfig: + unknownTimeout: 1m + scanInterval: 30s + + # This section represents the configuration of the Edge API that is served over HTTPS + api: + #(optional, default 90s) Alters how frequently heartbeat and last activity values are persisted + # activityUpdateInterval: 90s + #(optional, default 250) The number of API Sessions updated for last activity per transaction + # activityUpdateBatchSize: 250 + # sessionTimeout - optional, default 10m + # The number of minutes before an Edge API session will timeout. Timeouts are reset by + # API requests and connections that are maintained to Edge Routers + sessionTimeout: 30m + # address - required + # The default address (host:port) to use for enrollment for the Client API. This value must match one of the addresses + # defined in a bind point's address field for the `edge-client` API in the web section. + address: {{ .Host.PublicIp }}:1280 + # enrollment - required + # A section containing settings pertaining to enrollment. + enrollment: + # signingCert - required + # A Ziti Identity configuration section that specifically makes use of the cert and key fields to define + # a signing certificate from the PKI that the Ziti environment is using to sign certificates. The signingCert.cert + # will be added to the /.well-known CA store that is used to bootstrap trust with the Ziti Controller. + signingCert: + cert: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/certs/{{ .Component.Id }}.cert + key: /home/{{ .Model.MustVariable "credentials.ssh.username" }}/fablab/pki/{{ .Component.Id }}/keys/{{ .Component.Id }}.key + + # edgeIdentity - optional + # A section for identity enrollment specific settings + edgeIdentity: + # duration - optional, default 5m + # The length of time that a Ziti Edge Identity enrollment should remain valid. After + # this duration, the enrollment will expire and not longer be usable. + duration: 1h + # edgeRouter - Optional + # A section for edge router enrollment specific settings. + edgeRouter: + # duration - optional, default 5m + # The length of time that a Ziti Edge Router enrollment should remain valid. After + # this duration, the enrollment will expire and not longer be usable. + duration: 1h + + +# web - optional +# Defines webListeners that will be hosted by the controller. Each webListener can host many APIs and be bound to many +# bind points. +web: + # name - required + # Provides a name for this listener, used for logging output. Not required to be unique, but is highly suggested. + - name: all-apis-localhost + # bindPoints - required + # One or more bind points are required. A bind point specifies an interface (interface:port string) that defines + # where on the host machine the webListener will listen and the address (host:port) that should be used to + # publicly address the webListener(i.e. mydomain.com, localhost, 127.0.0.1). This public address may be used for + # incoming address resolution as well as used in responses in the API. + bindPoints: + #interface - required + # A host:port string on which network interface to listen on. 0.0.0.0 will listen on all interfaces + - interface: 0.0.0.0:1280 + + # address - required + # The public address that external incoming requests will be able to resolve. Used in request processing and + # response content that requires full host:port/path addresses. + address: {{ .Host.PublicIp }}:1280 + + # newAddress - optional + # A host:port string which will be sent out as an HTTP header "ziti-new-address" if specified. If the header + # is present, clients should update location configuration to immediately use the new address for future + # connections. The value of newAddress must be resolvable both via DNS and validate via certificates + #newAddress: localhost:1280 + # identity - optional + # Allows the webListener to have a specific identity instead of defaulting to the root `identity` section. + # identity: + # cert: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/certs/ctrl-client.cert.pem + # server_cert: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/certs/ctrl-server.cert.pem + # key: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/private/ctrl.key.pem + # ca: ${ZITI_SOURCE}/ziti/etc/ca/intermediate/certs/ca-chain.cert.pem + # options - optional + # Allows the specification of webListener level options - mainly dealing with HTTP/TLS settings. These options are + # used for all http servers started by the current webListener. + options: + # idleTimeout - optional, default 5000ms + # The maximum amount of idle time in milliseconds allowed for pipelined HTTP requests. Setting this too high + # can cause resources on the host to be consumed as clients remain connected and idle. Lowering this value + # will cause clients to reconnect on subsequent HTTPs requests. + idleTimeout: 5000ms #http timeouts, new + + # readTimeout - optional, default 5000ms + # The maximum amount of time in milliseconds http servers will wait to read the first incoming requests. A higher + # value risks consuming resources on the host with clients that are acting bad faith or suffering from high latency + # or packet loss. A lower value can risk losing connections to high latency/packet loss clients. + + readTimeout: 5000ms + # writeTimeout - optional, default 10000ms + # The total maximum time in milliseconds that the http server will wait for a single requests to be received and + # responded too. A higher value can allow long running requests to consume resources on the host. A lower value + # can risk ending requests before the server has a chance to respond. + + writeTimeout: 100000ms + # minTLSVersion - optional, default TSL1.2 + # The minimum version of TSL to support + + minTLSVersion: TLS1.2 + # maxTLSVersion - optional, default TSL1.3 + # The maximum version of TSL to support + + maxTLSVersion: TLS1.3 + # apis - required + # Allows one or more APIs to be bound to this webListener + apis: + # binding - required + # Specifies an API to bind to this webListener. Built-in APIs are + # - health-checks + # - edge-management + # - edge-client + # - fabric-management + - binding: health-checks + - binding: fabric + - binding: edge-management + - binding: edge-client + - binding: edge-oidc \ No newline at end of file diff --git a/zititest/models/sdk-status-test/configs/router.yml.tmpl b/zititest/models/sdk-status-test/configs/router.yml.tmpl new file mode 100644 index 000000000..f9ae60dbe --- /dev/null +++ b/zititest/models/sdk-status-test/configs/router.yml.tmpl @@ -0,0 +1,76 @@ +{{$ssh_username := .Model.MustVariable "credentials.ssh.username"}} +{{$identity := .Component.Id}} +{{$router_ip := .Host.PublicIp}} + +v: 3 + +enableDebugOps: true + +ha: + enabled: true + +identity: + cert: /home/{{$ssh_username}}/fablab/cfg/{{$identity}}-client.cert + server_cert: /home/{{$ssh_username}}/fablab/cfg/{{$identity}}-server.cert + key: /home/{{$ssh_username}}/fablab/cfg/{{$identity}}.key + ca: /home/{{$ssh_username}}/fablab/cfg/{{$identity}}-server.chain.pem + +ctrl: + endpoints: {{ range $host := .Model.MustSelectHosts "component.ctrl" 1 }} + - tls:{{ $host.PublicIp }}:6262{{end}} + +connectEvents: + fullSyncInterval: 10m + +healthChecks: + ctrlPingCheck: + # How often to ping the controller over the control channel. Defaults to 30 seconds + interval: 30s + # When to timeout the ping. Defaults to 15 seconds + timeout: 15s + # How long to wait before pinging the controller. Defaults to 15 seconds + initialDelay: 15s + +metrics: + reportInterval: 15s + messageQueueSize: 10 + +link: + listeners: + - binding: transport + bind: tls:0.0.0.0:6000 + advertise: tls:{{$router_ip}}:6000 + dialers: + - binding: transport + +listeners: +{{if .Component.HasTag "tunneler"}} + - binding: tunnel + options: + mode: host +{{end}} + - binding: edge + address: tls:0.0.0.0:6262 + options: + # (required) The public hostname and port combination that Ziti SDKs should connect on. Previously this was in the chanIngress section. + advertise: {{ .Host.PublicIp }}:6262 + +# By having an 'edge' section defined, the ziti router will attempt to parse the edge configuration. Removing this +# section, commenting out, or altering the name of the section will cause the router to no longer operate as an Edge +# Router. +edge: + # (required) Information used to generate the initial registration CSR. For documentation on these fields please + # refer to the openssl documentation. These values MUST be supplied and have no defaults. + csr: + country: US + province: NC + locality: Charlotte + organization: NetFoundry + organizationalUnit: Ziti + + # (required) SANs that this Gateways certs should contain. At least one IP or DNS SAN should be defined that matches + # the edge listeners "advertise" value from the "listeners" section. + sans: + ip: + - {{ .Host.PublicIp }} + diff --git a/zititest/models/sdk-status-test/main.go b/zititest/models/sdk-status-test/main.go new file mode 100644 index 000000000..b127d645a --- /dev/null +++ b/zititest/models/sdk-status-test/main.go @@ -0,0 +1,398 @@ +package main + +import ( + "embed" + _ "embed" + "fmt" + "github.com/michaelquigley/pfxlog" + "github.com/openziti/fablab" + "github.com/openziti/fablab/kernel/lib/actions" + "github.com/openziti/fablab/kernel/lib/actions/component" + "github.com/openziti/fablab/kernel/lib/actions/host" + "github.com/openziti/fablab/kernel/lib/actions/semaphore" + "github.com/openziti/fablab/kernel/lib/binding" + "github.com/openziti/fablab/kernel/lib/runlevel/0_infrastructure/aws_ssh_key" + "github.com/openziti/fablab/kernel/lib/runlevel/0_infrastructure/semaphore" + "github.com/openziti/fablab/kernel/lib/runlevel/0_infrastructure/terraform" + distribution "github.com/openziti/fablab/kernel/lib/runlevel/3_distribution" + "github.com/openziti/fablab/kernel/lib/runlevel/3_distribution/rsync" + aws_ssh_key2 "github.com/openziti/fablab/kernel/lib/runlevel/6_disposal/aws_ssh_key" + "github.com/openziti/fablab/kernel/lib/runlevel/6_disposal/terraform" + "github.com/openziti/fablab/kernel/model" + "github.com/openziti/fablab/resources" + "github.com/openziti/ziti/zititest/models/test_resources" + "github.com/openziti/ziti/zititest/zitilab" + zitilib_actions "github.com/openziti/ziti/zititest/zitilab/actions" + "github.com/openziti/ziti/zititest/zitilab/actions/edge" + "github.com/openziti/ziti/zititest/zitilab/chaos" + "github.com/openziti/ziti/zititest/zitilab/models" + "os" + "path" + "time" +) + +const TargetZitiVersion = "" + +//go:embed configs +var configResource embed.FS + +type scaleStrategy struct{} + +func (self scaleStrategy) IsScaled(entity model.Entity) bool { + if entity.GetType() == model.EntityTypeHost { + return entity.GetScope().HasTag("router") || entity.GetScope().HasTag("host") + } + return entity.GetType() == model.EntityTypeComponent && entity.GetScope().HasTag("host") +} + +func (self scaleStrategy) GetEntityCount(entity model.Entity) uint32 { + if entity.GetType() == model.EntityTypeHost { + if entity.GetScope().HasTag("router") { + return 2 + } + if entity.GetScope().HasTag("host") { + h := entity.(*model.Host) + if h.Region.Id == "us-east-1" { + return 8 + } + return 6 + } + } + if entity.GetType() == model.EntityTypeComponent { + return 10 + } + return 1 +} + +var m = &model.Model{ + Id: "sdk-status-test", + Scope: model.Scope{ + Defaults: model.Variables{ + "environment": "sdk-status-test", + "credentials": model.Variables{ + "aws": model.Variables{ + "managed_key": true, + }, + "ssh": model.Variables{ + "username": "ubuntu", + }, + "edge": model.Variables{ + "username": "admin", + "password": "admin", + }, + }, + "metrics": model.Variables{ + "influxdb": model.Variables{ + "url": "http://localhost:8086", + "db": "ziti", + }, + }, + }, + }, + StructureFactories: []model.Factory{ + model.FactoryFunc(func(m *model.Model) error { + err := m.ForEachHost("component.router", 1, func(host *model.Host) error { + host.InstanceType = "c5.xlarge" + return nil + }) + + if err != nil { + return err + } + + err = m.ForEachComponent(".host", 1, func(c *model.Component) error { + c.Type.(*zitilab.ZitiTunnelType).Mode = zitilab.ZitiTunnelModeHost + return nil + }) + + if err != nil { + return err + } + + return m.ForEachHost("component.host", 1, func(host *model.Host) error { + host.InstanceType = "c5.xlarge" + return nil + }) + }), + model.NewScaleFactoryWithDefaultEntityFactory(&scaleStrategy{}), + }, + Resources: model.Resources{ + resources.Configs: resources.SubFolder(configResource, "configs"), + resources.Binaries: os.DirFS(path.Join(os.Getenv("GOPATH"), "bin")), + resources.Terraform: test_resources.TerraformResources(), + }, + Regions: model.Regions{ + "us-east-1": { + Region: "us-east-1", + Site: "us-east-1a", + Hosts: model.Hosts{ + "ctrl1": { + InstanceType: "c5.xlarge", + Components: model.Components{ + "ctrl1": { + Scope: model.Scope{Tags: model.Tags{"ctrl"}}, + Type: &zitilab.ControllerType{ + Version: TargetZitiVersion, + }, + }, + }, + }, + "ctrl2": { + InstanceType: "c5.xlarge", + Components: model.Components{ + "ctrl2": { + Scope: model.Scope{Tags: model.Tags{"ctrl"}}, + Type: &zitilab.ControllerType{ + Version: TargetZitiVersion, + }, + }, + }, + }, + "router-us-{{.ScaleIndex}}": { + Scope: model.Scope{Tags: model.Tags{"router"}}, + Components: model.Components{ + "router-us-{{.Host.ScaleIndex}}": { + Scope: model.Scope{Tags: model.Tags{"router"}}, + Type: &zitilab.RouterType{ + Version: TargetZitiVersion, + }, + }, + }, + }, + "host-us-{{ .ScaleIndex }}": { + Scope: model.Scope{Tags: model.Tags{"host"}}, + Components: model.Components{ + "host-us-{{ .Host.ScaleIndex }}-{{ .ScaleIndex }}": { + Scope: model.Scope{Tags: model.Tags{"host"}}, + Type: &zitilab.ZitiTunnelType{ + Version: TargetZitiVersion, + HA: true, + Count: 2, + }, + }, + }, + }, + }, + }, + "eu-west-2": { + Region: "us-west-2", + Site: "us-west-2a", + Hosts: model.Hosts{ + "ctrl3": { + InstanceType: "c5.xlarge", + Components: model.Components{ + "ctrl3": { + Scope: model.Scope{Tags: model.Tags{"ctrl"}}, + Type: &zitilab.ControllerType{ + Version: TargetZitiVersion, + }, + }, + }, + }, + "router-eu-{{.ScaleIndex}}": { + Scope: model.Scope{Tags: model.Tags{"router"}}, + Components: model.Components{ + "router-eu-{{.Host.ScaleIndex}}": { + Scope: model.Scope{Tags: model.Tags{"router"}}, + Type: &zitilab.RouterType{ + Version: TargetZitiVersion, + }, + }, + }, + }, + "host-eu-{{ .ScaleIndex }}": { + Scope: model.Scope{Tags: model.Tags{"host"}}, + Components: model.Components{ + "host-eu-{{ .Host.ScaleIndex }}-{{ .ScaleIndex }}": { + Scope: model.Scope{Tags: model.Tags{"host"}}, + Type: &zitilab.ZitiTunnelType{ + Version: TargetZitiVersion, + HA: true, + }, + }, + }, + }, + }, + }, + "ap-southeast-2": { + Region: "ap-southeast-2", + Site: "ap-southeast-2a", + Hosts: model.Hosts{ + "router-ap-{{.ScaleIndex}}": { + Scope: model.Scope{Tags: model.Tags{"router", "scaled"}}, + Components: model.Components{ + "router-ap-{{.Host.ScaleIndex}}": { + Scope: model.Scope{Tags: model.Tags{"router"}}, + Type: &zitilab.RouterType{ + Version: TargetZitiVersion, + }, + }, + }, + }, + "host-ap-{{ .ScaleIndex }}": { + Scope: model.Scope{Tags: model.Tags{"host", "scaled"}}, + Components: model.Components{ + "host-ap-{{ .Host.ScaleIndex }}-{{ .ScaleIndex }}": { + Scope: model.Scope{Tags: model.Tags{"host"}}, + Type: &zitilab.ZitiTunnelType{ + Version: TargetZitiVersion, + HA: true, + }, + }, + }, + }, + }, + }, + }, + + Actions: model.ActionBinders{ + "bootstrap": model.ActionBinder(func(m *model.Model) model.Action { + workflow := actions.Workflow() + + workflow.AddAction(component.StopInParallel("*", 300)) + workflow.AddAction(host.GroupExec("*", 25, "rm -f logs/* ctrl.db")) + workflow.AddAction(host.GroupExec("component.ctrl", 5, "rm -rf ./fablab/ctrldata")) + + workflow.AddAction(component.Start(".ctrl")) + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(edge.InitRaftController("#ctrl1")) + + workflow.AddAction(edge.ControllerAvailable("#ctrl1", 30*time.Second)) + + workflow.AddAction(edge.Login("#ctrl1")) + + workflow.AddAction(edge.InitEdgeRouters(models.RouterTag, 25)) + workflow.AddAction(edge.InitIdentities(".host", 25)) + + workflow.AddAction(zitilib_actions.Edge("create", "edge-router-policy", "all", "--edge-router-roles", "#all", "--identity-roles", "#all")) + workflow.AddAction(zitilib_actions.Edge("create", "service-edge-router-policy", "all", "--service-roles", "#all", "--edge-router-roles", "#all")) + + workflow.AddAction(zitilib_actions.Edge("create", "config", "host-config", "host.v1", ` + { + "address" : "localhost", + "port" : 8080, + "protocol" : "tcp" + }`)) + + workflow.AddAction(zitilib_actions.Edge("create", "service", "test-svc", "-c", "host-config")) + workflow.AddAction(zitilib_actions.Edge("create", "service-policy", "test-svc-policy", "Bind", + "--identity-roles", "#all", "--service-roles", "#all")) + + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(edge.RaftJoin("ctrl1", ".ctrl")) + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(component.StartInParallel(".router", 10)) + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(component.StartInParallel(".host", 50)) + + return workflow + }), + "stop": model.Bind(component.StopInParallelHostExclusive("*", 15)), + "clean": model.Bind(actions.Workflow( + component.StopInParallelHostExclusive("*", 15), + host.GroupExec("*", 25, "rm -f logs/*"), + )), + "login": model.Bind(edge.Login("#ctrl1")), + "login2": model.Bind(edge.Login("#ctrl2")), + "login3": model.Bind(edge.Login("#ctrl3")), + "restart": model.ActionBinder(func(run *model.Model) model.Action { + workflow := actions.Workflow() + workflow.AddAction(component.StopInParallel("*", 100)) + workflow.AddAction(host.GroupExec("*", 25, "rm -f logs/*")) + workflow.AddAction(component.Start(".ctrl")) + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(component.StartInParallel(".router", 10)) + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(component.StartInParallel(".host", 50)) + return workflow + }), + "sowChaos": model.Bind(model.ActionFunc(sowChaos)), + "validateUp": model.Bind(model.ActionFunc(func(run model.Run) error { + if err := chaos.ValidateUp(run, ".ctrl", 3, 15*time.Second); err != nil { + return err + } + err := run.GetModel().ForEachComponent(".ctrl", 3, func(c *model.Component) error { + return edge.ControllerAvailable(c.Id, 30*time.Second).Execute(run) + }) + if err != nil { + return err + } + if err := chaos.ValidateUp(run, ".router", 100, time.Minute); err != nil { + pfxlog.Logger().WithError(err).Error("validate up failed, trying to start all routers again") + return component.StartInParallel(".router", 100).Execute(run) + } + return nil + })), + "validate": model.Bind(model.ActionFunc(validateSdkStatus)), + "ensureAllStarted": model.ActionBinder(func(m *model.Model) model.Action { + workflow := actions.Workflow() + workflow.AddAction(component.Start(".ctrl")) + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(component.StartInParallel(".router", 10)) + workflow.AddAction(semaphore.Sleep(2 * time.Second)) + workflow.AddAction(component.StartInParallel(".host", 50)) + return workflow + }), + "sleep2Min": model.Bind(model.ActionFunc(func(run model.Run) error { + time.Sleep(2 * time.Minute) + return nil + })), + "testIteration": model.Bind(model.ActionFunc(func(run model.Run) error { + return run.GetModel().Exec(run, + "sowChaos", + "validate", + "sleep2Min", + "validate", + "ensureAllStarted", + "validateUp", + "validate", + ) + })), + }, + + Infrastructure: model.Stages{ + aws_ssh_key.Express(), + &terraform_0.Terraform{ + Retries: 3, + ReadyCheck: &semaphore_0.ReadyStage{ + MaxWait: 90 * time.Second, + }, + }, + }, + + Distribution: model.Stages{ + distribution.DistributeSshKey("*"), + rsync.RsyncStaged(), + }, + + Disposal: model.Stages{ + terraform.Dispose(), + aws_ssh_key2.Dispose(), + }, +} + +func getHostNames() []string { + var result []string + for i := 0; i < 8; i++ { + for j := 0; j < 10; j++ { + result = append(result, fmt.Sprintf("host-us-%d-%d", i, j)) + if i < 6 { + result = append(result, fmt.Sprintf("host-eu-%d-%d", i, j)) + result = append(result, fmt.Sprintf("host-ap-%d-%d", i, j)) + } + } + } + return result +} + +func main() { + m.AddActivationActions("stop", "bootstrap") + + model.AddBootstrapExtension(binding.AwsCredentialsLoader) + model.AddBootstrapExtension(aws_ssh_key.KeyManager) + + fablab.InitModel(m) + fablab.Run() +} diff --git a/zititest/models/sdk-status-test/validation.go b/zititest/models/sdk-status-test/validation.go new file mode 100644 index 000000000..30076bd52 --- /dev/null +++ b/zititest/models/sdk-status-test/validation.go @@ -0,0 +1,204 @@ +/* + Copyright NetFoundry Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package main + +import ( + "errors" + "fmt" + "github.com/michaelquigley/pfxlog" + "github.com/openziti/channel/v3" + "github.com/openziti/channel/v3/protobufs" + "github.com/openziti/fablab/kernel/model" + "github.com/openziti/ziti/common/pb/mgmt_pb" + "github.com/openziti/ziti/zitirest" + "github.com/openziti/ziti/zititest/zitilab/chaos" + "google.golang.org/protobuf/proto" + "math/rand" + "time" +) + +// start with a random scenario then cycle through them +var scenarioCounter = rand.Intn(7) + +func sowChaos(run model.Run) error { + var controllers []*model.Component + var err error + + scenarioCounter = (scenarioCounter + 1) % 7 + scenario := scenarioCounter + 1 + + if scenario&0b001 > 0 { + controllers, err = chaos.SelectRandom(run, ".ctrl", chaos.RandomInRange(1, 2)) + if err != nil { + return err + } + time.Sleep(5 * time.Second) + } + + var routers []*model.Component + if scenario&0b010 > 0 { + routers, err = chaos.SelectRandom(run, ".router", chaos.PercentageRange(10, 75)) + if err != nil { + return err + } + } + + var hosts []*model.Component + if scenario&0b100 > 0 { + hosts, err = chaos.SelectRandom(run, ".host", chaos.PercentageRange(10, 75)) + if err != nil { + return err + } + } + + fmt.Printf("stopping %d controllers, %d routers and %d hosts\n", len(controllers), len(routers), len(hosts)) + if err = chaos.RestartSelected(run, controllers, 3); err != nil { + return err + } + var toStop []*model.Component + toStop = append(toStop, routers...) + toStop = append(toStop, hosts...) + return chaos.StopSelected(run, toStop, 100) +} + +func validateSdkStatus(run model.Run) error { + ctrls := run.GetModel().SelectComponents(".ctrl") + errC := make(chan error, len(ctrls)) + deadline := time.Now().Add(8 * time.Minute) + for _, ctrl := range ctrls { + ctrlComponent := ctrl + go validateSdkStatusForCtrlWithChan(run, ctrlComponent, deadline, errC) + } + + for i := 0; i < len(ctrls); i++ { + err := <-errC + if err != nil { + return err + } + } + + return nil +} + +func validateSdkStatusForCtrlWithChan(run model.Run, c *model.Component, deadline time.Time, errC chan<- error) { + errC <- validateSdkStatusForCtrl(run, c, deadline) +} + +func validateSdkStatusForCtrl(run model.Run, c *model.Component, deadline time.Time) error { + clients, err := chaos.EnsureLoggedIntoCtrl(run, c, time.Minute) + if err != nil { + return err + } + + start := time.Now() + logger := pfxlog.Logger().WithField("ctrl", c.Id) + + for { + count, err := validateSdkStatuses(c.Id, clients) + if err == nil { + return nil + } + + if time.Now().After(deadline) { + return err + } + + logger.Infof("current count of invalid sdk statuses: %v, elapsed time: %v", count, time.Since(start)) + beforeLogin := time.Now() + clients, err = chaos.EnsureLoggedIntoCtrl(run, c, time.Minute) + if err != nil { + return err + } + elapsed := time.Since(beforeLogin) + if elapsed < 5*time.Second { + time.Sleep((5 * time.Second) - elapsed) + } + } +} + +func validateSdkStatuses(id string, clients *zitirest.Clients) (int, error) { + logger := pfxlog.Logger().WithField("ctrl", id) + + closeNotify := make(chan struct{}) + eventNotify := make(chan *mgmt_pb.RouterIdentityConnectionStatusesDetails, 1) + + handleSdkStatusResults := func(msg *channel.Message, _ channel.Channel) { + detail := &mgmt_pb.RouterIdentityConnectionStatusesDetails{} + if err := proto.Unmarshal(msg.Body, detail); err != nil { + pfxlog.Logger().WithError(err).Error("unable to unmarshal router sdk status details") + return + } + eventNotify <- detail + } + + bindHandler := func(binding channel.Binding) error { + binding.AddReceiveHandlerF(int32(mgmt_pb.ContentType_ValidateIdentityConnectionStatusesResultType), handleSdkStatusResults) + binding.AddCloseHandler(channel.CloseHandlerF(func(ch channel.Channel) { + close(closeNotify) + })) + return nil + } + + ch, err := clients.NewWsMgmtChannel(channel.BindHandlerF(bindHandler)) + if err != nil { + return 0, err + } + + defer func() { + _ = ch.Close() + }() + + request := &mgmt_pb.ValidateIdentityConnectionStatusesRequest{ + RouterFilter: "limit none", + } + responseMsg, err := protobufs.MarshalTyped(request).WithTimeout(10 * time.Second).SendForReply(ch) + + response := &mgmt_pb.ValidateIdentityConnectionStatusesResponse{} + if err = protobufs.TypedResponse(response).Unmarshall(responseMsg, err); err != nil { + return 0, err + } + + if !response.Success { + return 0, fmt.Errorf("failed to start sdk statuses validation: %s", response.Message) + } + + logger.Infof("started validation of %v routers", response.ComponentCount) + + expected := response.ComponentCount + + invalid := 0 + for expected > 0 { + select { + case <-closeNotify: + fmt.Printf("channel closed, exiting") + return 0, errors.New("unexpected close of mgmt channel") + case routerDetail := <-eventNotify: + if len(routerDetail.Errors) > 0 { + for _, errMsg := range routerDetail.Errors { + logger.Infof("router %s (%s) reported error: %s", routerDetail.ComponentId, routerDetail.ComponentName, errMsg) + invalid++ + } + } + expected-- + } + } + if invalid == 0 { + logger.Infof("sdk status validation of %v routers successful", response.ComponentCount) + return invalid, nil + } + return invalid, fmt.Errorf("invalid sdk statuses found") +} diff --git a/zititest/models/smoke/actions/bootstrap.go b/zititest/models/smoke/actions/bootstrap.go index 0bed0db51..b29ba4af3 100644 --- a/zititest/models/smoke/actions/bootstrap.go +++ b/zititest/models/smoke/actions/bootstrap.go @@ -55,7 +55,7 @@ func (a *bootstrapAction) bind(m *model.Model) model.Action { if isHA { workflow.AddAction(semaphore.Sleep(10 * time.Second)) - workflow.AddAction(edge.RaftJoin(".ctrl")) + workflow.AddAction(edge.RaftJoin("ctrl1", ".ctrl")) workflow.AddAction(semaphore.Sleep(2 * time.Second)) workflow.AddAction(edge.InitRaftController("#ctrl1")) } diff --git a/zititest/zitilab/actions/edge/raft_join.go b/zititest/zitilab/actions/edge/raft_join.go index d788633d9..65a229cf6 100644 --- a/zititest/zitilab/actions/edge/raft_join.go +++ b/zititest/zitilab/actions/edge/raft_join.go @@ -8,32 +8,41 @@ import ( "github.com/pkg/errors" ) -func RaftJoin(componentSpec string) model.Action { +func RaftJoin(primaryId string, componentSpec string) model.Action { return &raftJoin{ + primaryId: primaryId, componentSpec: componentSpec, } } type raftJoin struct { + primaryId string componentSpec string } func (self *raftJoin) Execute(run model.Run) error { + primary, err := run.GetModel().SelectComponent(self.primaryId) + if err != nil { + return fmt.Errorf("could not find primary controller component with id '%s'", self.primaryId) + } + ctrls := run.GetModel().SelectComponents(self.componentSpec) if len(ctrls) < 1 { return errors.Errorf("no controllers found with spec '%v'", self.componentSpec) } - primary := ctrls[0] ctrlType, ok := primary.Type.(*zitilab.ControllerType) if !ok { return errors.Errorf("component %s is not a controller", primary.Id) } log := pfxlog.Logger().WithField("component", primary.Id) - for _, c := range ctrls[1:] { + for _, c := range ctrls { + if c.Id == primary.Id { + continue + } tmpl := "%s agent cluster add %v --id %v" cmd := fmt.Sprintf(tmpl, ctrlType.GetBinaryPath(primary), "tls:"+c.Host.PublicIp+":6262", c.Id) log.Info(cmd) - if err := primary.GetHost().ExecLogOnlyOnError(cmd); err != nil { + if err = primary.GetHost().ExecLogOnlyOnError(cmd); err != nil { return err } } diff --git a/zititest/zitilab/chaos/chaos.go b/zititest/zitilab/chaos/chaos.go index 03219da33..5e5f05bf4 100644 --- a/zititest/zitilab/chaos/chaos.go +++ b/zititest/zitilab/chaos/chaos.go @@ -20,6 +20,7 @@ import ( "fmt" "github.com/michaelquigley/pfxlog" "github.com/openziti/fablab/kernel/model" + "github.com/openziti/ziti/zitirest" "math/rand" "time" ) @@ -29,6 +30,21 @@ func StaticNumber(val int) func(int) int { return val } } + +func RandomInRange(min, max int) func(int) int { + return func(count int) int { + if count > max { + count = max + } + + if count <= min { + return count + } + + return rand.Intn(count-min) + min + } +} + func RandomOfTotal() func(count int) int { return func(count int) int { return rand.Intn(count) + 1 @@ -75,6 +91,34 @@ func SelectRandom(run model.Run, selector string, f func(count int) int) ([]*mod return result, nil } +func StopSelected(run model.Run, list []*model.Component, concurrency int) error { + if len(list) == 0 { + return nil + } + return run.GetModel().ForEachComponentIn(list, concurrency, func(c *model.Component) error { + if _, ok := c.Type.(model.ServerComponent); ok { + if err := c.Type.Stop(run, c); err != nil { + return err + } + + for { + isRunning, err := c.IsRunning(run) + if err != nil { + return err + } + if !isRunning { + break + } else { + time.Sleep(250 * time.Millisecond) + } + } + time.Sleep(time.Second) + return nil + } + return fmt.Errorf("component %v isn't of ServerComponent type, is of type %T", c, c.Type) + }) +} + func RestartSelected(run model.Run, list []*model.Component, concurrency int) error { if len(list) == 0 { return nil @@ -127,3 +171,56 @@ func ValidateUp(run model.Run, spec string, concurrency int, timeout time.Durati } return err } + +func EnsureLoggedIntoCtrl(run model.Run, c *model.Component, timeout time.Duration) (*zitirest.Clients, error) { + username := c.MustStringVariable("credentials.edge.username") + password := c.MustStringVariable("credentials.edge.password") + edgeApiBaseUrl := c.Host.PublicIp + ":1280" + + var clients *zitirest.Clients + loginStart := time.Now() + for { + var err error + clients, err = zitirest.NewManagementClients(edgeApiBaseUrl) + if err != nil { + if time.Since(loginStart) > timeout { + return nil, err + } + pfxlog.Logger().WithField("ctrlId", c.Id).WithError(err).Info("failed to initialize mgmt client, trying again in 1s") + if err = EnsureRunning(c, run); err != nil { + pfxlog.Logger().WithField("ctrlId", c.Id).WithError(err).Info("error while trying to ensure ctrl running") + } + time.Sleep(time.Second) + continue + } + + if err = clients.Authenticate(username, password); err != nil { + if time.Since(loginStart) > timeout { + return nil, err + } + pfxlog.Logger().WithField("ctrlId", c.Id).WithError(err).Info("failed to authenticate, trying again in 1s") + if err = EnsureRunning(c, run); err != nil { + pfxlog.Logger().WithField("ctrlId", c.Id).WithError(err).Info("error while trying to ensure ctrl running") + } + time.Sleep(time.Second) + } else { + break + } + } + return clients, nil +} + +func EnsureRunning(c *model.Component, run model.Run) error { + if sc, ok := c.Type.(model.ServerComponent); ok { + isRunning, err := c.IsRunning(run) + if err != nil { + return err + } + if isRunning { + return nil + } + time.Sleep(time.Second) + return sc.Start(run, c) + } + return fmt.Errorf("component %v isn't of ServerComponent type, is of type %T", c, c.Type) +} diff --git a/zititest/zitilab/component_ziti_tunnel.go b/zititest/zitilab/component_ziti_tunnel.go index ffabd3ebd..b10b1df15 100644 --- a/zititest/zitilab/component_ziti_tunnel.go +++ b/zititest/zitilab/component_ziti_tunnel.go @@ -55,6 +55,7 @@ type ZitiTunnelType struct { LocalPath string ConfigPathF func(c *model.Component) string HA bool + Count uint8 } func (self *ZitiTunnelType) Label() string { @@ -73,6 +74,9 @@ func (self *ZitiTunnelType) GetActions() map[string]model.ComponentAction { func (self *ZitiTunnelType) InitType(*model.Component) { canonicalizeGoAppVersion(&self.Version) + if self.Count < 1 { + self.Count = 1 + } } func (self *ZitiTunnelType) Dump() any { @@ -114,13 +118,36 @@ func (self *ZitiTunnelType) GetConfigPath(c *model.Component) string { } func (self *ZitiTunnelType) Start(_ model.Run, c *model.Component) error { + pids, err := c.GetHost().FindProcesses(self.getProcessFilter(c)) + if err != nil { + return err + } + if len(pids) >= int(self.Count) { + fmt.Printf("ziti tunnel(s) %s already started\n", c.Id) + return nil + } + + count := int(self.Count) - len(pids) + start := 0 + if len(pids) > 0 { + start = int(self.Count) + } + for n := range count { + if err = self.StartIndividual(c, start+n); err != nil { + return err + } + } + return nil +} + +func (self *ZitiTunnelType) StartIndividual(c *model.Component, idx int) error { mode := self.Mode user := c.GetHost().GetSshUser() binaryPath := GetZitiBinaryPath(c, self.Version) configPath := self.GetConfigPath(c) - logsPath := fmt.Sprintf("/home/%s/logs/%s.log", user, c.Id) + logsPath := fmt.Sprintf("/home/%s/logs/%s-%v.log", user, c.Id, idx) useSudo := "" if mode == ZitiTunnelModeTproxy {