diff --git a/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service.pb.go b/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service.pb.go index dc5511ee404ca..00856f7ef886a 100644 --- a/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service.pb.go @@ -337,6 +337,89 @@ func (x *GetDownloadBaseUrlResponse) GetBaseUrl() string { return "" } +// Request for GetInstallationMetadata. +type GetInstallationMetadataRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetInstallationMetadataRequest) Reset() { + *x = GetInstallationMetadataRequest{} + mi := &file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetInstallationMetadataRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetInstallationMetadataRequest) ProtoMessage() {} + +func (x *GetInstallationMetadataRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_msgTypes[6] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetInstallationMetadataRequest.ProtoReflect.Descriptor instead. +func (*GetInstallationMetadataRequest) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDescGZIP(), []int{6} +} + +// Response for GetInstallationMetadata. +type GetInstallationMetadataResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Determines whether updates should target a per-machine installation. + IsPerMachineInstall bool `protobuf:"varint,1,opt,name=is_per_machine_install,json=isPerMachineInstall,proto3" json:"is_per_machine_install,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetInstallationMetadataResponse) Reset() { + *x = GetInstallationMetadataResponse{} + mi := &file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetInstallationMetadataResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetInstallationMetadataResponse) ProtoMessage() {} + +func (x *GetInstallationMetadataResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_msgTypes[7] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetInstallationMetadataResponse.ProtoReflect.Descriptor instead. +func (*GetInstallationMetadataResponse) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDescGZIP(), []int{7} +} + +func (x *GetInstallationMetadataResponse) GetIsPerMachineInstall() bool { + if x != nil { + return x.IsPerMachineInstall + } + return false +} + var File_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto protoreflect.FileDescriptor const file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDesc = "" + @@ -358,10 +441,14 @@ const file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDes "\rerror_message\x18\x02 \x01(\tR\ferrorMessage\"\x1b\n" + "\x19GetDownloadBaseUrlRequest\"7\n" + "\x1aGetDownloadBaseUrlResponse\x12\x19\n" + - "\bbase_url\x18\x01 \x01(\tR\abaseUrl2\xc7\x02\n" + + "\bbase_url\x18\x01 \x01(\tR\abaseUrl\" \n" + + "\x1eGetInstallationMetadataRequest\"V\n" + + "\x1fGetInstallationMetadataResponse\x123\n" + + "\x16is_per_machine_install\x18\x01 \x01(\bR\x13isPerMachineInstall2\xf0\x03\n" + "\x11AutoUpdateService\x12\x97\x01\n" + "\x12GetClusterVersions\x12?.teleport.lib.teleterm.auto_update.v1.GetClusterVersionsRequest\x1a@.teleport.lib.teleterm.auto_update.v1.GetClusterVersionsResponse\x12\x97\x01\n" + - "\x12GetDownloadBaseUrl\x12?.teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlRequest\x1a@.teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponseBcZagithub.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/auto_update/v1;auto_updatev1b\x06proto3" + "\x12GetDownloadBaseUrl\x12?.teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlRequest\x1a@.teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponse\x12\xa6\x01\n" + + "\x17GetInstallationMetadata\x12D.teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest\x1aE.teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponseBcZagithub.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/auto_update/v1;auto_updatev1b\x06proto3" var ( file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDescOnce sync.Once @@ -375,24 +462,28 @@ func file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDesc return file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDescData } -var file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_goTypes = []any{ - (*GetClusterVersionsRequest)(nil), // 0: teleport.lib.teleterm.auto_update.v1.GetClusterVersionsRequest - (*GetClusterVersionsResponse)(nil), // 1: teleport.lib.teleterm.auto_update.v1.GetClusterVersionsResponse - (*ClusterVersionInfo)(nil), // 2: teleport.lib.teleterm.auto_update.v1.ClusterVersionInfo - (*UnreachableCluster)(nil), // 3: teleport.lib.teleterm.auto_update.v1.UnreachableCluster - (*GetDownloadBaseUrlRequest)(nil), // 4: teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlRequest - (*GetDownloadBaseUrlResponse)(nil), // 5: teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponse + (*GetClusterVersionsRequest)(nil), // 0: teleport.lib.teleterm.auto_update.v1.GetClusterVersionsRequest + (*GetClusterVersionsResponse)(nil), // 1: teleport.lib.teleterm.auto_update.v1.GetClusterVersionsResponse + (*ClusterVersionInfo)(nil), // 2: teleport.lib.teleterm.auto_update.v1.ClusterVersionInfo + (*UnreachableCluster)(nil), // 3: teleport.lib.teleterm.auto_update.v1.UnreachableCluster + (*GetDownloadBaseUrlRequest)(nil), // 4: teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlRequest + (*GetDownloadBaseUrlResponse)(nil), // 5: teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponse + (*GetInstallationMetadataRequest)(nil), // 6: teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest + (*GetInstallationMetadataResponse)(nil), // 7: teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse } var file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_depIdxs = []int32{ 2, // 0: teleport.lib.teleterm.auto_update.v1.GetClusterVersionsResponse.reachable_clusters:type_name -> teleport.lib.teleterm.auto_update.v1.ClusterVersionInfo 3, // 1: teleport.lib.teleterm.auto_update.v1.GetClusterVersionsResponse.unreachable_clusters:type_name -> teleport.lib.teleterm.auto_update.v1.UnreachableCluster 0, // 2: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetClusterVersions:input_type -> teleport.lib.teleterm.auto_update.v1.GetClusterVersionsRequest 4, // 3: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetDownloadBaseUrl:input_type -> teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlRequest - 1, // 4: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetClusterVersions:output_type -> teleport.lib.teleterm.auto_update.v1.GetClusterVersionsResponse - 5, // 5: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetDownloadBaseUrl:output_type -> teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponse - 4, // [4:6] is the sub-list for method output_type - 2, // [2:4] is the sub-list for method input_type + 6, // 4: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetInstallationMetadata:input_type -> teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest + 1, // 5: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetClusterVersions:output_type -> teleport.lib.teleterm.auto_update.v1.GetClusterVersionsResponse + 5, // 6: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetDownloadBaseUrl:output_type -> teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponse + 7, // 7: teleport.lib.teleterm.auto_update.v1.AutoUpdateService.GetInstallationMetadata:output_type -> teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse + 5, // [5:8] is the sub-list for method output_type + 2, // [2:5] is the sub-list for method input_type 2, // [2:2] is the sub-list for extension type_name 2, // [2:2] is the sub-list for extension extendee 0, // [0:2] is the sub-list for field type_name @@ -409,7 +500,7 @@ func file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_init() GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDesc), len(file_teleport_lib_teleterm_auto_update_v1_auto_update_service_proto_rawDesc)), NumEnums: 0, - NumMessages: 6, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service_grpc.pb.go index a178f81d09a3a..018d1014bb54b 100644 --- a/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/auto_update/v1/auto_update_service_grpc.pb.go @@ -35,8 +35,9 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - AutoUpdateService_GetClusterVersions_FullMethodName = "/teleport.lib.teleterm.auto_update.v1.AutoUpdateService/GetClusterVersions" - AutoUpdateService_GetDownloadBaseUrl_FullMethodName = "/teleport.lib.teleterm.auto_update.v1.AutoUpdateService/GetDownloadBaseUrl" + AutoUpdateService_GetClusterVersions_FullMethodName = "/teleport.lib.teleterm.auto_update.v1.AutoUpdateService/GetClusterVersions" + AutoUpdateService_GetDownloadBaseUrl_FullMethodName = "/teleport.lib.teleterm.auto_update.v1.AutoUpdateService/GetDownloadBaseUrl" + AutoUpdateService_GetInstallationMetadata_FullMethodName = "/teleport.lib.teleterm.auto_update.v1.AutoUpdateService/GetInstallationMetadata" ) // AutoUpdateServiceClient is the client API for AutoUpdateService service. @@ -51,6 +52,9 @@ type AutoUpdateServiceClient interface { // Can be overridden with TELEPORT_CDN_BASE_URL env var. // OSS builds require this env var to be set, otherwise an error is returned. GetDownloadBaseUrl(ctx context.Context, in *GetDownloadBaseUrlRequest, opts ...grpc.CallOption) (*GetDownloadBaseUrlResponse, error) + // GetInstallationMetadata returns installation metadata of the currently running app instance. + // Implemented only on Windows. + GetInstallationMetadata(ctx context.Context, in *GetInstallationMetadataRequest, opts ...grpc.CallOption) (*GetInstallationMetadataResponse, error) } type autoUpdateServiceClient struct { @@ -81,6 +85,16 @@ func (c *autoUpdateServiceClient) GetDownloadBaseUrl(ctx context.Context, in *Ge return out, nil } +func (c *autoUpdateServiceClient) GetInstallationMetadata(ctx context.Context, in *GetInstallationMetadataRequest, opts ...grpc.CallOption) (*GetInstallationMetadataResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(GetInstallationMetadataResponse) + err := c.cc.Invoke(ctx, AutoUpdateService_GetInstallationMetadata_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // AutoUpdateServiceServer is the server API for AutoUpdateService service. // All implementations must embed UnimplementedAutoUpdateServiceServer // for forward compatibility. @@ -93,6 +107,9 @@ type AutoUpdateServiceServer interface { // Can be overridden with TELEPORT_CDN_BASE_URL env var. // OSS builds require this env var to be set, otherwise an error is returned. GetDownloadBaseUrl(context.Context, *GetDownloadBaseUrlRequest) (*GetDownloadBaseUrlResponse, error) + // GetInstallationMetadata returns installation metadata of the currently running app instance. + // Implemented only on Windows. + GetInstallationMetadata(context.Context, *GetInstallationMetadataRequest) (*GetInstallationMetadataResponse, error) mustEmbedUnimplementedAutoUpdateServiceServer() } @@ -109,6 +126,9 @@ func (UnimplementedAutoUpdateServiceServer) GetClusterVersions(context.Context, func (UnimplementedAutoUpdateServiceServer) GetDownloadBaseUrl(context.Context, *GetDownloadBaseUrlRequest) (*GetDownloadBaseUrlResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetDownloadBaseUrl not implemented") } +func (UnimplementedAutoUpdateServiceServer) GetInstallationMetadata(context.Context, *GetInstallationMetadataRequest) (*GetInstallationMetadataResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetInstallationMetadata not implemented") +} func (UnimplementedAutoUpdateServiceServer) mustEmbedUnimplementedAutoUpdateServiceServer() {} func (UnimplementedAutoUpdateServiceServer) testEmbeddedByValue() {} @@ -166,6 +186,24 @@ func _AutoUpdateService_GetDownloadBaseUrl_Handler(srv interface{}, ctx context. return interceptor(ctx, in, info, handler) } +func _AutoUpdateService_GetInstallationMetadata_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetInstallationMetadataRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AutoUpdateServiceServer).GetInstallationMetadata(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AutoUpdateService_GetInstallationMetadata_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AutoUpdateServiceServer).GetInstallationMetadata(ctx, req.(*GetInstallationMetadataRequest)) + } + return interceptor(ctx, in, info, handler) +} + // AutoUpdateService_ServiceDesc is the grpc.ServiceDesc for AutoUpdateService service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -181,6 +219,10 @@ var AutoUpdateService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetDownloadBaseUrl", Handler: _AutoUpdateService_GetDownloadBaseUrl_Handler, }, + { + MethodName: "GetInstallationMetadata", + Handler: _AutoUpdateService_GetInstallationMetadata_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "teleport/lib/teleterm/auto_update/v1/auto_update_service.proto", diff --git a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go index 4b75f84e51f31..eeb7b410340a8 100644 --- a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service.pb.go @@ -100,6 +100,56 @@ func (BackgroundItemStatus) EnumDescriptor() ([]byte, []int) { return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{0} } +// WindowsServiceStatus maps to service-related errors in golang.org/x/sys/windows/zerrors_windows.go. +type WindowsServiceStatus int32 + +const ( + WindowsServiceStatus_WINDOWS_SERVICE_STATUS_UNSPECIFIED WindowsServiceStatus = 0 + WindowsServiceStatus_WINDOWS_SERVICE_STATUS_OK WindowsServiceStatus = 1 + WindowsServiceStatus_WINDOWS_SERVICE_STATUS_DOES_NOT_EXIST WindowsServiceStatus = 2 +) + +// Enum value maps for WindowsServiceStatus. +var ( + WindowsServiceStatus_name = map[int32]string{ + 0: "WINDOWS_SERVICE_STATUS_UNSPECIFIED", + 1: "WINDOWS_SERVICE_STATUS_OK", + 2: "WINDOWS_SERVICE_STATUS_DOES_NOT_EXIST", + } + WindowsServiceStatus_value = map[string]int32{ + "WINDOWS_SERVICE_STATUS_UNSPECIFIED": 0, + "WINDOWS_SERVICE_STATUS_OK": 1, + "WINDOWS_SERVICE_STATUS_DOES_NOT_EXIST": 2, + } +) + +func (x WindowsServiceStatus) Enum() *WindowsServiceStatus { + p := new(WindowsServiceStatus) + *p = x + return p +} + +func (x WindowsServiceStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (WindowsServiceStatus) Descriptor() protoreflect.EnumDescriptor { + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_enumTypes[1].Descriptor() +} + +func (WindowsServiceStatus) Type() protoreflect.EnumType { + return &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_enumTypes[1] +} + +func (x WindowsServiceStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use WindowsServiceStatus.Descriptor instead. +func (WindowsServiceStatus) EnumDescriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{1} +} + // Request for Start. type StartRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -443,6 +493,111 @@ func (x *GetBackgroundItemStatusResponse) GetStatus() BackgroundItemStatus { return BackgroundItemStatus_BACKGROUND_ITEM_STATUS_UNSPECIFIED } +// Request for CheckInstallTimeRequirementsRequest. +type CheckInstallTimeRequirementsRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CheckInstallTimeRequirementsRequest) Reset() { + *x = CheckInstallTimeRequirementsRequest{} + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CheckInstallTimeRequirementsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckInstallTimeRequirementsRequest) ProtoMessage() {} + +func (x *CheckInstallTimeRequirementsRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[8] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckInstallTimeRequirementsRequest.ProtoReflect.Descriptor instead. +func (*CheckInstallTimeRequirementsRequest) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{8} +} + +// Response for CheckInstallTimeRequirementsResponse. +type CheckInstallTimeRequirementsResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Types that are valid to be assigned to Status: + // + // *CheckInstallTimeRequirementsResponse_WindowsServiceStatus + Status isCheckInstallTimeRequirementsResponse_Status `protobuf_oneof:"status"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CheckInstallTimeRequirementsResponse) Reset() { + *x = CheckInstallTimeRequirementsResponse{} + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CheckInstallTimeRequirementsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CheckInstallTimeRequirementsResponse) ProtoMessage() {} + +func (x *CheckInstallTimeRequirementsResponse) ProtoReflect() protoreflect.Message { + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CheckInstallTimeRequirementsResponse.ProtoReflect.Descriptor instead. +func (*CheckInstallTimeRequirementsResponse) Descriptor() ([]byte, []int) { + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{9} +} + +func (x *CheckInstallTimeRequirementsResponse) GetStatus() isCheckInstallTimeRequirementsResponse_Status { + if x != nil { + return x.Status + } + return nil +} + +func (x *CheckInstallTimeRequirementsResponse) GetWindowsServiceStatus() WindowsServiceStatus { + if x != nil { + if x, ok := x.Status.(*CheckInstallTimeRequirementsResponse_WindowsServiceStatus); ok { + return x.WindowsServiceStatus + } + } + return WindowsServiceStatus_WINDOWS_SERVICE_STATUS_UNSPECIFIED +} + +type isCheckInstallTimeRequirementsResponse_Status interface { + isCheckInstallTimeRequirementsResponse_Status() +} + +type CheckInstallTimeRequirementsResponse_WindowsServiceStatus struct { + WindowsServiceStatus WindowsServiceStatus `protobuf:"varint,1,opt,name=windows_service_status,json=windowsServiceStatus,proto3,enum=teleport.lib.teleterm.vnet.v1.WindowsServiceStatus,oneof"` +} + +func (*CheckInstallTimeRequirementsResponse_WindowsServiceStatus) isCheckInstallTimeRequirementsResponse_Status() { +} + // Request for RunDiagnostics. type RunDiagnosticsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -452,7 +607,7 @@ type RunDiagnosticsRequest struct { func (x *RunDiagnosticsRequest) Reset() { *x = RunDiagnosticsRequest{} - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[8] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -464,7 +619,7 @@ func (x *RunDiagnosticsRequest) String() string { func (*RunDiagnosticsRequest) ProtoMessage() {} func (x *RunDiagnosticsRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[8] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -477,7 +632,7 @@ func (x *RunDiagnosticsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RunDiagnosticsRequest.ProtoReflect.Descriptor instead. func (*RunDiagnosticsRequest) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{8} + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{10} } // Response for RunDiagnostics. @@ -490,7 +645,7 @@ type RunDiagnosticsResponse struct { func (x *RunDiagnosticsResponse) Reset() { *x = RunDiagnosticsResponse{} - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[9] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -502,7 +657,7 @@ func (x *RunDiagnosticsResponse) String() string { func (*RunDiagnosticsResponse) ProtoMessage() {} func (x *RunDiagnosticsResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[9] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -515,7 +670,7 @@ func (x *RunDiagnosticsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunDiagnosticsResponse.ProtoReflect.Descriptor instead. func (*RunDiagnosticsResponse) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{9} + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{11} } func (x *RunDiagnosticsResponse) GetReport() *v1.Report { @@ -534,7 +689,7 @@ type AutoConfigureSSHRequest struct { func (x *AutoConfigureSSHRequest) Reset() { *x = AutoConfigureSSHRequest{} - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[10] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -546,7 +701,7 @@ func (x *AutoConfigureSSHRequest) String() string { func (*AutoConfigureSSHRequest) ProtoMessage() {} func (x *AutoConfigureSSHRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[10] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -559,7 +714,7 @@ func (x *AutoConfigureSSHRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoConfigureSSHRequest.ProtoReflect.Descriptor instead. func (*AutoConfigureSSHRequest) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{10} + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{12} } // Response for AutoConfigureSSH. @@ -571,7 +726,7 @@ type AutoConfigureSSHResponse struct { func (x *AutoConfigureSSHResponse) Reset() { *x = AutoConfigureSSHResponse{} - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[11] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -583,7 +738,7 @@ func (x *AutoConfigureSSHResponse) String() string { func (*AutoConfigureSSHResponse) ProtoMessage() {} func (x *AutoConfigureSSHResponse) ProtoReflect() protoreflect.Message { - mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[11] + mi := &file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -596,7 +751,7 @@ func (x *AutoConfigureSSHResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AutoConfigureSSHResponse.ProtoReflect.Descriptor instead. func (*AutoConfigureSSHResponse) Descriptor() ([]byte, []int) { - return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{11} + return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP(), []int{13} } var File_teleport_lib_teleterm_vnet_v1_vnet_service_proto protoreflect.FileDescriptor @@ -616,7 +771,11 @@ const file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDesc = "" + "\x14vnet_ssh_config_path\x18\x04 \x01(\tR\x11vnetSshConfigPath\" \n" + "\x1eGetBackgroundItemStatusRequest\"n\n" + "\x1fGetBackgroundItemStatusResponse\x12K\n" + - "\x06status\x18\x01 \x01(\x0e23.teleport.lib.teleterm.vnet.v1.BackgroundItemStatusR\x06status\"\x17\n" + + "\x06status\x18\x01 \x01(\x0e23.teleport.lib.teleterm.vnet.v1.BackgroundItemStatusR\x06status\"%\n" + + "#CheckInstallTimeRequirementsRequest\"\x9d\x01\n" + + "$CheckInstallTimeRequirementsResponse\x12k\n" + + "\x16windows_service_status\x18\x01 \x01(\x0e23.teleport.lib.teleterm.vnet.v1.WindowsServiceStatusH\x00R\x14windowsServiceStatusB\b\n" + + "\x06status\"\x17\n" + "\x15RunDiagnosticsRequest\"S\n" + "\x16RunDiagnosticsResponse\x129\n" + "\x06report\x18\x01 \x01(\v2!.teleport.lib.vnet.diag.v1.ReportR\x06report\"\x19\n" + @@ -628,12 +787,17 @@ const file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDesc = "" + "\x1eBACKGROUND_ITEM_STATUS_ENABLED\x10\x02\x12,\n" + "(BACKGROUND_ITEM_STATUS_REQUIRES_APPROVAL\x10\x03\x12$\n" + " BACKGROUND_ITEM_STATUS_NOT_FOUND\x10\x04\x12(\n" + - "$BACKGROUND_ITEM_STATUS_NOT_SUPPORTED\x10\x052\xf1\x05\n" + + "$BACKGROUND_ITEM_STATUS_NOT_SUPPORTED\x10\x05*\x88\x01\n" + + "\x14WindowsServiceStatus\x12&\n" + + "\"WINDOWS_SERVICE_STATUS_UNSPECIFIED\x10\x00\x12\x1d\n" + + "\x19WINDOWS_SERVICE_STATUS_OK\x10\x01\x12)\n" + + "%WINDOWS_SERVICE_STATUS_DOES_NOT_EXIST\x10\x022\x9b\a\n" + "\vVnetService\x12b\n" + "\x05Start\x12+.teleport.lib.teleterm.vnet.v1.StartRequest\x1a,.teleport.lib.teleterm.vnet.v1.StartResponse\x12_\n" + "\x04Stop\x12*.teleport.lib.teleterm.vnet.v1.StopRequest\x1a+.teleport.lib.teleterm.vnet.v1.StopResponse\x12}\n" + "\x0eGetServiceInfo\x124.teleport.lib.teleterm.vnet.v1.GetServiceInfoRequest\x1a5.teleport.lib.teleterm.vnet.v1.GetServiceInfoResponse\x12\x98\x01\n" + - "\x17GetBackgroundItemStatus\x12=.teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest\x1a>.teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse\x12}\n" + + "\x17GetBackgroundItemStatus\x12=.teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest\x1a>.teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse\x12\xa7\x01\n" + + "\x1cCheckInstallTimeRequirements\x12B.teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest\x1aC.teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse\x12}\n" + "\x0eRunDiagnostics\x124.teleport.lib.teleterm.vnet.v1.RunDiagnosticsRequest\x1a5.teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse\x12\x83\x01\n" + "\x10AutoConfigureSSH\x126.teleport.lib.teleterm.vnet.v1.AutoConfigureSSHRequest\x1a7.teleport.lib.teleterm.vnet.v1.AutoConfigureSSHResponseBUZSgithub.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/vnet/v1;vnetv1b\x06proto3" @@ -649,44 +813,50 @@ func file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescGZIP() []byte return file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDescData } -var file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes = make([]protoimpl.MessageInfo, 12) +var file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes = make([]protoimpl.MessageInfo, 14) var file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_goTypes = []any{ - (BackgroundItemStatus)(0), // 0: teleport.lib.teleterm.vnet.v1.BackgroundItemStatus - (*StartRequest)(nil), // 1: teleport.lib.teleterm.vnet.v1.StartRequest - (*StartResponse)(nil), // 2: teleport.lib.teleterm.vnet.v1.StartResponse - (*StopRequest)(nil), // 3: teleport.lib.teleterm.vnet.v1.StopRequest - (*StopResponse)(nil), // 4: teleport.lib.teleterm.vnet.v1.StopResponse - (*GetServiceInfoRequest)(nil), // 5: teleport.lib.teleterm.vnet.v1.GetServiceInfoRequest - (*GetServiceInfoResponse)(nil), // 6: teleport.lib.teleterm.vnet.v1.GetServiceInfoResponse - (*GetBackgroundItemStatusRequest)(nil), // 7: teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest - (*GetBackgroundItemStatusResponse)(nil), // 8: teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse - (*RunDiagnosticsRequest)(nil), // 9: teleport.lib.teleterm.vnet.v1.RunDiagnosticsRequest - (*RunDiagnosticsResponse)(nil), // 10: teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse - (*AutoConfigureSSHRequest)(nil), // 11: teleport.lib.teleterm.vnet.v1.AutoConfigureSSHRequest - (*AutoConfigureSSHResponse)(nil), // 12: teleport.lib.teleterm.vnet.v1.AutoConfigureSSHResponse - (*v1.Report)(nil), // 13: teleport.lib.vnet.diag.v1.Report + (BackgroundItemStatus)(0), // 0: teleport.lib.teleterm.vnet.v1.BackgroundItemStatus + (WindowsServiceStatus)(0), // 1: teleport.lib.teleterm.vnet.v1.WindowsServiceStatus + (*StartRequest)(nil), // 2: teleport.lib.teleterm.vnet.v1.StartRequest + (*StartResponse)(nil), // 3: teleport.lib.teleterm.vnet.v1.StartResponse + (*StopRequest)(nil), // 4: teleport.lib.teleterm.vnet.v1.StopRequest + (*StopResponse)(nil), // 5: teleport.lib.teleterm.vnet.v1.StopResponse + (*GetServiceInfoRequest)(nil), // 6: teleport.lib.teleterm.vnet.v1.GetServiceInfoRequest + (*GetServiceInfoResponse)(nil), // 7: teleport.lib.teleterm.vnet.v1.GetServiceInfoResponse + (*GetBackgroundItemStatusRequest)(nil), // 8: teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest + (*GetBackgroundItemStatusResponse)(nil), // 9: teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse + (*CheckInstallTimeRequirementsRequest)(nil), // 10: teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest + (*CheckInstallTimeRequirementsResponse)(nil), // 11: teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse + (*RunDiagnosticsRequest)(nil), // 12: teleport.lib.teleterm.vnet.v1.RunDiagnosticsRequest + (*RunDiagnosticsResponse)(nil), // 13: teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse + (*AutoConfigureSSHRequest)(nil), // 14: teleport.lib.teleterm.vnet.v1.AutoConfigureSSHRequest + (*AutoConfigureSSHResponse)(nil), // 15: teleport.lib.teleterm.vnet.v1.AutoConfigureSSHResponse + (*v1.Report)(nil), // 16: teleport.lib.vnet.diag.v1.Report } var file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_depIdxs = []int32{ 0, // 0: teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse.status:type_name -> teleport.lib.teleterm.vnet.v1.BackgroundItemStatus - 13, // 1: teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse.report:type_name -> teleport.lib.vnet.diag.v1.Report - 1, // 2: teleport.lib.teleterm.vnet.v1.VnetService.Start:input_type -> teleport.lib.teleterm.vnet.v1.StartRequest - 3, // 3: teleport.lib.teleterm.vnet.v1.VnetService.Stop:input_type -> teleport.lib.teleterm.vnet.v1.StopRequest - 5, // 4: teleport.lib.teleterm.vnet.v1.VnetService.GetServiceInfo:input_type -> teleport.lib.teleterm.vnet.v1.GetServiceInfoRequest - 7, // 5: teleport.lib.teleterm.vnet.v1.VnetService.GetBackgroundItemStatus:input_type -> teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest - 9, // 6: teleport.lib.teleterm.vnet.v1.VnetService.RunDiagnostics:input_type -> teleport.lib.teleterm.vnet.v1.RunDiagnosticsRequest - 11, // 7: teleport.lib.teleterm.vnet.v1.VnetService.AutoConfigureSSH:input_type -> teleport.lib.teleterm.vnet.v1.AutoConfigureSSHRequest - 2, // 8: teleport.lib.teleterm.vnet.v1.VnetService.Start:output_type -> teleport.lib.teleterm.vnet.v1.StartResponse - 4, // 9: teleport.lib.teleterm.vnet.v1.VnetService.Stop:output_type -> teleport.lib.teleterm.vnet.v1.StopResponse - 6, // 10: teleport.lib.teleterm.vnet.v1.VnetService.GetServiceInfo:output_type -> teleport.lib.teleterm.vnet.v1.GetServiceInfoResponse - 8, // 11: teleport.lib.teleterm.vnet.v1.VnetService.GetBackgroundItemStatus:output_type -> teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse - 10, // 12: teleport.lib.teleterm.vnet.v1.VnetService.RunDiagnostics:output_type -> teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse - 12, // 13: teleport.lib.teleterm.vnet.v1.VnetService.AutoConfigureSSH:output_type -> teleport.lib.teleterm.vnet.v1.AutoConfigureSSHResponse - 8, // [8:14] is the sub-list for method output_type - 2, // [2:8] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 1, // 1: teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse.windows_service_status:type_name -> teleport.lib.teleterm.vnet.v1.WindowsServiceStatus + 16, // 2: teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse.report:type_name -> teleport.lib.vnet.diag.v1.Report + 2, // 3: teleport.lib.teleterm.vnet.v1.VnetService.Start:input_type -> teleport.lib.teleterm.vnet.v1.StartRequest + 4, // 4: teleport.lib.teleterm.vnet.v1.VnetService.Stop:input_type -> teleport.lib.teleterm.vnet.v1.StopRequest + 6, // 5: teleport.lib.teleterm.vnet.v1.VnetService.GetServiceInfo:input_type -> teleport.lib.teleterm.vnet.v1.GetServiceInfoRequest + 8, // 6: teleport.lib.teleterm.vnet.v1.VnetService.GetBackgroundItemStatus:input_type -> teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest + 10, // 7: teleport.lib.teleterm.vnet.v1.VnetService.CheckInstallTimeRequirements:input_type -> teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest + 12, // 8: teleport.lib.teleterm.vnet.v1.VnetService.RunDiagnostics:input_type -> teleport.lib.teleterm.vnet.v1.RunDiagnosticsRequest + 14, // 9: teleport.lib.teleterm.vnet.v1.VnetService.AutoConfigureSSH:input_type -> teleport.lib.teleterm.vnet.v1.AutoConfigureSSHRequest + 3, // 10: teleport.lib.teleterm.vnet.v1.VnetService.Start:output_type -> teleport.lib.teleterm.vnet.v1.StartResponse + 5, // 11: teleport.lib.teleterm.vnet.v1.VnetService.Stop:output_type -> teleport.lib.teleterm.vnet.v1.StopResponse + 7, // 12: teleport.lib.teleterm.vnet.v1.VnetService.GetServiceInfo:output_type -> teleport.lib.teleterm.vnet.v1.GetServiceInfoResponse + 9, // 13: teleport.lib.teleterm.vnet.v1.VnetService.GetBackgroundItemStatus:output_type -> teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse + 11, // 14: teleport.lib.teleterm.vnet.v1.VnetService.CheckInstallTimeRequirements:output_type -> teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse + 13, // 15: teleport.lib.teleterm.vnet.v1.VnetService.RunDiagnostics:output_type -> teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse + 15, // 16: teleport.lib.teleterm.vnet.v1.VnetService.AutoConfigureSSH:output_type -> teleport.lib.teleterm.vnet.v1.AutoConfigureSSHResponse + 10, // [10:17] is the sub-list for method output_type + 3, // [3:10] is the sub-list for method input_type + 3, // [3:3] is the sub-list for extension type_name + 3, // [3:3] is the sub-list for extension extendee + 0, // [0:3] is the sub-list for field type_name } func init() { file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_init() } @@ -694,13 +864,16 @@ func file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_init() { if File_teleport_lib_teleterm_vnet_v1_vnet_service_proto != nil { return } + file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_msgTypes[9].OneofWrappers = []any{ + (*CheckInstallTimeRequirementsResponse_WindowsServiceStatus)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDesc), len(file_teleport_lib_teleterm_vnet_v1_vnet_service_proto_rawDesc)), - NumEnums: 1, - NumMessages: 12, + NumEnums: 2, + NumMessages: 14, NumExtensions: 0, NumServices: 1, }, diff --git a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go index 074693d5c6c11..a40c0b4ada577 100644 --- a/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go +++ b/gen/proto/go/teleport/lib/teleterm/vnet/v1/vnet_service_grpc.pb.go @@ -35,12 +35,13 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - VnetService_Start_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/Start" - VnetService_Stop_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/Stop" - VnetService_GetServiceInfo_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/GetServiceInfo" - VnetService_GetBackgroundItemStatus_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/GetBackgroundItemStatus" - VnetService_RunDiagnostics_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/RunDiagnostics" - VnetService_AutoConfigureSSH_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/AutoConfigureSSH" + VnetService_Start_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/Start" + VnetService_Stop_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/Stop" + VnetService_GetServiceInfo_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/GetServiceInfo" + VnetService_GetBackgroundItemStatus_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/GetBackgroundItemStatus" + VnetService_CheckInstallTimeRequirements_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/CheckInstallTimeRequirements" + VnetService_RunDiagnostics_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/RunDiagnostics" + VnetService_AutoConfigureSSH_FullMethodName = "/teleport.lib.teleterm.vnet.v1.VnetService/AutoConfigureSSH" ) // VnetServiceClient is the client API for VnetService service. @@ -58,6 +59,9 @@ type VnetServiceClient interface { // GetBackgroundItemStatus returns the status of the background item responsible for launching // VNet daemon. macOS only. tsh must be compiled with the vnetdaemon build tag. GetBackgroundItemStatus(ctx context.Context, in *GetBackgroundItemStatusRequest, opts ...grpc.CallOption) (*GetBackgroundItemStatusResponse, error) + // CheckInstallTimeRequirements validates install-time prerequisites (for example, VNet service presence) that can + // only be changed by reinstalling the app. + CheckInstallTimeRequirements(ctx context.Context, in *CheckInstallTimeRequirementsRequest, opts ...grpc.CallOption) (*CheckInstallTimeRequirementsResponse, error) // RunDiagnostics runs a set of heuristics to determine if VNet actually works on the device, that // is receives network traffic and DNS queries. RunDiagnostics requires VNet to be started. RunDiagnostics(ctx context.Context, in *RunDiagnosticsRequest, opts ...grpc.CallOption) (*RunDiagnosticsResponse, error) @@ -114,6 +118,16 @@ func (c *vnetServiceClient) GetBackgroundItemStatus(ctx context.Context, in *Get return out, nil } +func (c *vnetServiceClient) CheckInstallTimeRequirements(ctx context.Context, in *CheckInstallTimeRequirementsRequest, opts ...grpc.CallOption) (*CheckInstallTimeRequirementsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CheckInstallTimeRequirementsResponse) + err := c.cc.Invoke(ctx, VnetService_CheckInstallTimeRequirements_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *vnetServiceClient) RunDiagnostics(ctx context.Context, in *RunDiagnosticsRequest, opts ...grpc.CallOption) (*RunDiagnosticsResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(RunDiagnosticsResponse) @@ -149,6 +163,9 @@ type VnetServiceServer interface { // GetBackgroundItemStatus returns the status of the background item responsible for launching // VNet daemon. macOS only. tsh must be compiled with the vnetdaemon build tag. GetBackgroundItemStatus(context.Context, *GetBackgroundItemStatusRequest) (*GetBackgroundItemStatusResponse, error) + // CheckInstallTimeRequirements validates install-time prerequisites (for example, VNet service presence) that can + // only be changed by reinstalling the app. + CheckInstallTimeRequirements(context.Context, *CheckInstallTimeRequirementsRequest) (*CheckInstallTimeRequirementsResponse, error) // RunDiagnostics runs a set of heuristics to determine if VNet actually works on the device, that // is receives network traffic and DNS queries. RunDiagnostics requires VNet to be started. RunDiagnostics(context.Context, *RunDiagnosticsRequest) (*RunDiagnosticsResponse, error) @@ -177,6 +194,9 @@ func (UnimplementedVnetServiceServer) GetServiceInfo(context.Context, *GetServic func (UnimplementedVnetServiceServer) GetBackgroundItemStatus(context.Context, *GetBackgroundItemStatusRequest) (*GetBackgroundItemStatusResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method GetBackgroundItemStatus not implemented") } +func (UnimplementedVnetServiceServer) CheckInstallTimeRequirements(context.Context, *CheckInstallTimeRequirementsRequest) (*CheckInstallTimeRequirementsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CheckInstallTimeRequirements not implemented") +} func (UnimplementedVnetServiceServer) RunDiagnostics(context.Context, *RunDiagnosticsRequest) (*RunDiagnosticsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method RunDiagnostics not implemented") } @@ -276,6 +296,24 @@ func _VnetService_GetBackgroundItemStatus_Handler(srv interface{}, ctx context.C return interceptor(ctx, in, info, handler) } +func _VnetService_CheckInstallTimeRequirements_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CheckInstallTimeRequirementsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(VnetServiceServer).CheckInstallTimeRequirements(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: VnetService_CheckInstallTimeRequirements_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(VnetServiceServer).CheckInstallTimeRequirements(ctx, req.(*CheckInstallTimeRequirementsRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _VnetService_RunDiagnostics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(RunDiagnosticsRequest) if err := dec(in); err != nil { @@ -335,6 +373,10 @@ var VnetService_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetBackgroundItemStatus", Handler: _VnetService_GetBackgroundItemStatus_Handler, }, + { + MethodName: "CheckInstallTimeRequirements", + Handler: _VnetService_CheckInstallTimeRequirements_Handler, + }, { MethodName: "RunDiagnostics", Handler: _VnetService_RunDiagnostics_Handler, diff --git a/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.client.ts b/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.client.ts index 667d930a94edf..1bfefff32f917 100644 --- a/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.client.ts +++ b/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.client.ts @@ -23,6 +23,8 @@ import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; import { AutoUpdateService } from "./auto_update_service_pb"; +import type { GetInstallationMetadataResponse } from "./auto_update_service_pb"; +import type { GetInstallationMetadataRequest } from "./auto_update_service_pb"; import type { GetDownloadBaseUrlResponse } from "./auto_update_service_pb"; import type { GetDownloadBaseUrlRequest } from "./auto_update_service_pb"; import { stackIntercept } from "@protobuf-ts/runtime-rpc"; @@ -50,6 +52,13 @@ export interface IAutoUpdateServiceClient { * @generated from protobuf rpc: GetDownloadBaseUrl(teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlRequest) returns (teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponse); */ getDownloadBaseUrl(input: GetDownloadBaseUrlRequest, options?: RpcOptions): UnaryCall; + /** + * GetInstallationMetadata returns installation metadata of the currently running app instance. + * Implemented only on Windows. + * + * @generated from protobuf rpc: GetInstallationMetadata(teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest) returns (teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse); + */ + getInstallationMetadata(input: GetInstallationMetadataRequest, options?: RpcOptions): UnaryCall; } /** * AutoUpdateService provides access to information about client tools updates. @@ -82,4 +91,14 @@ export class AutoUpdateServiceClient implements IAutoUpdateServiceClient, Servic const method = this.methods[1], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } + /** + * GetInstallationMetadata returns installation metadata of the currently running app instance. + * Implemented only on Windows. + * + * @generated from protobuf rpc: GetInstallationMetadata(teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest) returns (teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse); + */ + getInstallationMetadata(input: GetInstallationMetadataRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[2], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); + } } diff --git a/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.grpc-server.ts b/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.grpc-server.ts index f48a7368e2cd2..5fcd5888834eb 100644 --- a/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.grpc-server.ts +++ b/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.grpc-server.ts @@ -20,6 +20,8 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // +import { GetInstallationMetadataResponse } from "./auto_update_service_pb"; +import { GetInstallationMetadataRequest } from "./auto_update_service_pb"; import { GetDownloadBaseUrlResponse } from "./auto_update_service_pb"; import { GetDownloadBaseUrlRequest } from "./auto_update_service_pb"; import { GetClusterVersionsResponse } from "./auto_update_service_pb"; @@ -45,6 +47,13 @@ export interface IAutoUpdateService extends grpc.UntypedServiceImplementation { * @generated from protobuf rpc: GetDownloadBaseUrl(teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlRequest) returns (teleport.lib.teleterm.auto_update.v1.GetDownloadBaseUrlResponse); */ getDownloadBaseUrl: grpc.handleUnaryCall; + /** + * GetInstallationMetadata returns installation metadata of the currently running app instance. + * Implemented only on Windows. + * + * @generated from protobuf rpc: GetInstallationMetadata(teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest) returns (teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse); + */ + getInstallationMetadata: grpc.handleUnaryCall; } /** * @grpc/grpc-js definition for the protobuf service teleport.lib.teleterm.auto_update.v1.AutoUpdateService. @@ -77,5 +86,15 @@ export const autoUpdateServiceDefinition: grpc.ServiceDefinition GetDownloadBaseUrlRequest.fromBinary(bytes), responseSerialize: value => Buffer.from(GetDownloadBaseUrlResponse.toBinary(value)), requestSerialize: value => Buffer.from(GetDownloadBaseUrlRequest.toBinary(value)) + }, + getInstallationMetadata: { + path: "/teleport.lib.teleterm.auto_update.v1.AutoUpdateService/GetInstallationMetadata", + originalName: "GetInstallationMetadata", + requestStream: false, + responseStream: false, + responseDeserialize: bytes => GetInstallationMetadataResponse.fromBinary(bytes), + requestDeserialize: bytes => GetInstallationMetadataRequest.fromBinary(bytes), + responseSerialize: value => Buffer.from(GetInstallationMetadataResponse.toBinary(value)), + requestSerialize: value => Buffer.from(GetInstallationMetadataRequest.toBinary(value)) } }; diff --git a/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.ts b/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.ts index 0e4814b83caca..83801d7bbb41e 100644 --- a/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.ts +++ b/gen/proto/ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb.ts @@ -120,6 +120,26 @@ export interface GetDownloadBaseUrlResponse { */ baseUrl: string; } +/** + * Request for GetInstallationMetadata. + * + * @generated from protobuf message teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest + */ +export interface GetInstallationMetadataRequest { +} +/** + * Response for GetInstallationMetadata. + * + * @generated from protobuf message teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse + */ +export interface GetInstallationMetadataResponse { + /** + * Determines whether updates should target a per-machine installation. + * + * @generated from protobuf field: bool is_per_machine_install = 1; + */ + isPerMachineInstall: boolean; +} // @generated message type with reflection information, may provide speed optimized methods class GetClusterVersionsRequest$Type extends MessageType { constructor() { @@ -398,10 +418,83 @@ class GetDownloadBaseUrlResponse$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest", []); + } + create(value?: PartialMessage): GetInstallationMetadataRequest { + const message = globalThis.Object.create((this.messagePrototype!)); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetInstallationMetadataRequest): GetInstallationMetadataRequest { + return target ?? this.create(); + } + internalBinaryWrite(message: GetInstallationMetadataRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataRequest + */ +export const GetInstallationMetadataRequest = new GetInstallationMetadataRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class GetInstallationMetadataResponse$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse", [ + { no: 1, name: "is_per_machine_install", kind: "scalar", T: 8 /*ScalarType.BOOL*/ } + ]); + } + create(value?: PartialMessage): GetInstallationMetadataResponse { + const message = globalThis.Object.create((this.messagePrototype!)); + message.isPerMachineInstall = false; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: GetInstallationMetadataResponse): GetInstallationMetadataResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* bool is_per_machine_install */ 1: + message.isPerMachineInstall = reader.bool(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: GetInstallationMetadataResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* bool is_per_machine_install = 1; */ + if (message.isPerMachineInstall !== false) + writer.tag(1, WireType.Varint).bool(message.isPerMachineInstall); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.auto_update.v1.GetInstallationMetadataResponse + */ +export const GetInstallationMetadataResponse = new GetInstallationMetadataResponse$Type(); /** * @generated ServiceType for protobuf service teleport.lib.teleterm.auto_update.v1.AutoUpdateService */ export const AutoUpdateService = new ServiceType("teleport.lib.teleterm.auto_update.v1.AutoUpdateService", [ { name: "GetClusterVersions", options: {}, I: GetClusterVersionsRequest, O: GetClusterVersionsResponse }, - { name: "GetDownloadBaseUrl", options: {}, I: GetDownloadBaseUrlRequest, O: GetDownloadBaseUrlResponse } + { name: "GetDownloadBaseUrl", options: {}, I: GetDownloadBaseUrlRequest, O: GetDownloadBaseUrlResponse }, + { name: "GetInstallationMetadata", options: {}, I: GetInstallationMetadataRequest, O: GetInstallationMetadataResponse } ]); diff --git a/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.client.ts b/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.client.ts index bd3b7b8620f20..ba1851a0d4e7b 100644 --- a/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.client.ts +++ b/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.client.ts @@ -27,6 +27,8 @@ import type { AutoConfigureSSHResponse } from "./vnet_service_pb"; import type { AutoConfigureSSHRequest } from "./vnet_service_pb"; import type { RunDiagnosticsResponse } from "./vnet_service_pb"; import type { RunDiagnosticsRequest } from "./vnet_service_pb"; +import type { CheckInstallTimeRequirementsResponse } from "./vnet_service_pb"; +import type { CheckInstallTimeRequirementsRequest } from "./vnet_service_pb"; import type { GetBackgroundItemStatusResponse } from "./vnet_service_pb"; import type { GetBackgroundItemStatusRequest } from "./vnet_service_pb"; import type { GetServiceInfoResponse } from "./vnet_service_pb"; @@ -69,6 +71,13 @@ export interface IVnetServiceClient { * @generated from protobuf rpc: GetBackgroundItemStatus(teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest) returns (teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse); */ getBackgroundItemStatus(input: GetBackgroundItemStatusRequest, options?: RpcOptions): UnaryCall; + /** + * CheckInstallTimeRequirements validates install-time prerequisites (for example, VNet service presence) that can + * only be changed by reinstalling the app. + * + * @generated from protobuf rpc: CheckInstallTimeRequirements(teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest) returns (teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse); + */ + checkInstallTimeRequirements(input: CheckInstallTimeRequirementsRequest, options?: RpcOptions): UnaryCall; /** * RunDiagnostics runs a set of heuristics to determine if VNet actually works on the device, that * is receives network traffic and DNS queries. RunDiagnostics requires VNet to be started. @@ -132,6 +141,16 @@ export class VnetServiceClient implements IVnetServiceClient, ServiceInfo { const method = this.methods[3], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } + /** + * CheckInstallTimeRequirements validates install-time prerequisites (for example, VNet service presence) that can + * only be changed by reinstalling the app. + * + * @generated from protobuf rpc: CheckInstallTimeRequirements(teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest) returns (teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse); + */ + checkInstallTimeRequirements(input: CheckInstallTimeRequirementsRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[4], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); + } /** * RunDiagnostics runs a set of heuristics to determine if VNet actually works on the device, that * is receives network traffic and DNS queries. RunDiagnostics requires VNet to be started. @@ -139,7 +158,7 @@ export class VnetServiceClient implements IVnetServiceClient, ServiceInfo { * @generated from protobuf rpc: RunDiagnostics(teleport.lib.teleterm.vnet.v1.RunDiagnosticsRequest) returns (teleport.lib.teleterm.vnet.v1.RunDiagnosticsResponse); */ runDiagnostics(input: RunDiagnosticsRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[4], opt = this._transport.mergeOptions(options); + const method = this.methods[5], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** @@ -149,7 +168,7 @@ export class VnetServiceClient implements IVnetServiceClient, ServiceInfo { * @generated from protobuf rpc: AutoConfigureSSH(teleport.lib.teleterm.vnet.v1.AutoConfigureSSHRequest) returns (teleport.lib.teleterm.vnet.v1.AutoConfigureSSHResponse); */ autoConfigureSSH(input: AutoConfigureSSHRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[5], opt = this._transport.mergeOptions(options); + const method = this.methods[6], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } } diff --git a/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.grpc-server.ts b/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.grpc-server.ts index ea46ece0fcd38..4d0760e6268b6 100644 --- a/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.grpc-server.ts +++ b/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.grpc-server.ts @@ -24,6 +24,8 @@ import { AutoConfigureSSHResponse } from "./vnet_service_pb"; import { AutoConfigureSSHRequest } from "./vnet_service_pb"; import { RunDiagnosticsResponse } from "./vnet_service_pb"; import { RunDiagnosticsRequest } from "./vnet_service_pb"; +import { CheckInstallTimeRequirementsResponse } from "./vnet_service_pb"; +import { CheckInstallTimeRequirementsRequest } from "./vnet_service_pb"; import { GetBackgroundItemStatusResponse } from "./vnet_service_pb"; import { GetBackgroundItemStatusRequest } from "./vnet_service_pb"; import { GetServiceInfoResponse } from "./vnet_service_pb"; @@ -64,6 +66,13 @@ export interface IVnetService extends grpc.UntypedServiceImplementation { * @generated from protobuf rpc: GetBackgroundItemStatus(teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusRequest) returns (teleport.lib.teleterm.vnet.v1.GetBackgroundItemStatusResponse); */ getBackgroundItemStatus: grpc.handleUnaryCall; + /** + * CheckInstallTimeRequirements validates install-time prerequisites (for example, VNet service presence) that can + * only be changed by reinstalling the app. + * + * @generated from protobuf rpc: CheckInstallTimeRequirements(teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest) returns (teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse); + */ + checkInstallTimeRequirements: grpc.handleUnaryCall; /** * RunDiagnostics runs a set of heuristics to determine if VNet actually works on the device, that * is receives network traffic and DNS queries. RunDiagnostics requires VNet to be started. @@ -131,6 +140,16 @@ export const vnetServiceDefinition: grpc.ServiceDefinition = { responseSerialize: value => Buffer.from(GetBackgroundItemStatusResponse.toBinary(value)), requestSerialize: value => Buffer.from(GetBackgroundItemStatusRequest.toBinary(value)) }, + checkInstallTimeRequirements: { + path: "/teleport.lib.teleterm.vnet.v1.VnetService/CheckInstallTimeRequirements", + originalName: "CheckInstallTimeRequirements", + requestStream: false, + responseStream: false, + responseDeserialize: bytes => CheckInstallTimeRequirementsResponse.fromBinary(bytes), + requestDeserialize: bytes => CheckInstallTimeRequirementsRequest.fromBinary(bytes), + responseSerialize: value => Buffer.from(CheckInstallTimeRequirementsResponse.toBinary(value)), + requestSerialize: value => Buffer.from(CheckInstallTimeRequirementsRequest.toBinary(value)) + }, runDiagnostics: { path: "/teleport.lib.teleterm.vnet.v1.VnetService/RunDiagnostics", originalName: "RunDiagnostics", diff --git a/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.ts b/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.ts index 9a58c635851c0..552d9a1d87892 100644 --- a/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.ts +++ b/gen/proto/ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb.ts @@ -118,6 +118,32 @@ export interface GetBackgroundItemStatusResponse { */ status: BackgroundItemStatus; } +/** + * Request for CheckInstallTimeRequirementsRequest. + * + * @generated from protobuf message teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest + */ +export interface CheckInstallTimeRequirementsRequest { +} +/** + * Response for CheckInstallTimeRequirementsResponse. + * + * @generated from protobuf message teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse + */ +export interface CheckInstallTimeRequirementsResponse { + /** + * @generated from protobuf oneof: status + */ + status: { + oneofKind: "windowsServiceStatus"; + /** + * @generated from protobuf field: teleport.lib.teleterm.vnet.v1.WindowsServiceStatus windows_service_status = 1; + */ + windowsServiceStatus: WindowsServiceStatus; + } | { + oneofKind: undefined; + }; +} /** * Request for RunDiagnostics. * @@ -185,6 +211,25 @@ export enum BackgroundItemStatus { */ NOT_SUPPORTED = 5 } +/** + * WindowsServiceStatus maps to service-related errors in golang.org/x/sys/windows/zerrors_windows.go. + * + * @generated from protobuf enum teleport.lib.teleterm.vnet.v1.WindowsServiceStatus + */ +export enum WindowsServiceStatus { + /** + * @generated from protobuf enum value: WINDOWS_SERVICE_STATUS_UNSPECIFIED = 0; + */ + UNSPECIFIED = 0, + /** + * @generated from protobuf enum value: WINDOWS_SERVICE_STATUS_OK = 1; + */ + OK = 1, + /** + * @generated from protobuf enum value: WINDOWS_SERVICE_STATUS_DOES_NOT_EXIST = 2; + */ + DOES_NOT_EXIST = 2 +} // @generated message type with reflection information, may provide speed optimized methods class StartRequest$Type extends MessageType { constructor() { @@ -454,6 +499,81 @@ class GetBackgroundItemStatusResponse$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest", []); + } + create(value?: PartialMessage): CheckInstallTimeRequirementsRequest { + const message = globalThis.Object.create((this.messagePrototype!)); + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CheckInstallTimeRequirementsRequest): CheckInstallTimeRequirementsRequest { + return target ?? this.create(); + } + internalBinaryWrite(message: CheckInstallTimeRequirementsRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsRequest + */ +export const CheckInstallTimeRequirementsRequest = new CheckInstallTimeRequirementsRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class CheckInstallTimeRequirementsResponse$Type extends MessageType { + constructor() { + super("teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse", [ + { no: 1, name: "windows_service_status", kind: "enum", oneof: "status", T: () => ["teleport.lib.teleterm.vnet.v1.WindowsServiceStatus", WindowsServiceStatus, "WINDOWS_SERVICE_STATUS_"] } + ]); + } + create(value?: PartialMessage): CheckInstallTimeRequirementsResponse { + const message = globalThis.Object.create((this.messagePrototype!)); + message.status = { oneofKind: undefined }; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: CheckInstallTimeRequirementsResponse): CheckInstallTimeRequirementsResponse { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* teleport.lib.teleterm.vnet.v1.WindowsServiceStatus windows_service_status */ 1: + message.status = { + oneofKind: "windowsServiceStatus", + windowsServiceStatus: reader.int32() + }; + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: CheckInstallTimeRequirementsResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* teleport.lib.teleterm.vnet.v1.WindowsServiceStatus windows_service_status = 1; */ + if (message.status.oneofKind === "windowsServiceStatus") + writer.tag(1, WireType.Varint).int32(message.status.windowsServiceStatus); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message teleport.lib.teleterm.vnet.v1.CheckInstallTimeRequirementsResponse + */ +export const CheckInstallTimeRequirementsResponse = new CheckInstallTimeRequirementsResponse$Type(); +// @generated message type with reflection information, may provide speed optimized methods class RunDiagnosticsRequest$Type extends MessageType { constructor() { super("teleport.lib.teleterm.vnet.v1.RunDiagnosticsRequest", []); @@ -582,6 +702,7 @@ export const VnetService = new ServiceType("teleport.lib.teleterm.vnet.v1.VnetSe { name: "Stop", options: {}, I: StopRequest, O: StopResponse }, { name: "GetServiceInfo", options: {}, I: GetServiceInfoRequest, O: GetServiceInfoResponse }, { name: "GetBackgroundItemStatus", options: {}, I: GetBackgroundItemStatusRequest, O: GetBackgroundItemStatusResponse }, + { name: "CheckInstallTimeRequirements", options: {}, I: CheckInstallTimeRequirementsRequest, O: CheckInstallTimeRequirementsResponse }, { name: "RunDiagnostics", options: {}, I: RunDiagnosticsRequest, O: RunDiagnosticsResponse }, { name: "AutoConfigureSSH", options: {}, I: AutoConfigureSSHRequest, O: AutoConfigureSSHResponse } ]); diff --git a/lib/teleterm/autoupdate/service.go b/lib/teleterm/autoupdate/service.go index f124d8904618d..b666f8d09e3c4 100644 --- a/lib/teleterm/autoupdate/service.go +++ b/lib/teleterm/autoupdate/service.go @@ -138,9 +138,7 @@ func (s *Service) GetDownloadBaseUrl(_ context.Context, _ *api.GetDownloadBaseUr return nil, trace.Wrap(err) } - return &api.GetDownloadBaseUrlResponse{ - BaseUrl: baseURL, - }, trace.Wrap(err) + return &api.GetDownloadBaseUrlResponse{BaseUrl: baseURL}, nil } // resolveBaseURL generates the base URL using the same logic as the teleport/lib/autoupdate/tools package. diff --git a/lib/teleterm/autoupdate/service_windows.go b/lib/teleterm/autoupdate/service_windows.go new file mode 100644 index 0000000000000..0fff264627efc --- /dev/null +++ b/lib/teleterm/autoupdate/service_windows.go @@ -0,0 +1,89 @@ +// Teleport +// Copyright (C) 2026 Gravitational, Inc. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package autoupdate + +import ( + "context" + "errors" + "os" + "path/filepath" + + "github.com/gravitational/trace" + "golang.org/x/sys/windows/registry" + + api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/auto_update/v1" +) + +const ( + // Defined in electron-builder-config.js + teleportConnectGUID = "22539266-67e8-54a3-83b9-dfdca7b33ee1" + teleportConnectKeyPath = `SOFTWARE\` + teleportConnectGUID + installLocationKey = "InstallLocation" +) + +// GetInstallationMetadata returns installation metadata of the currently running app instance. +func (s *Service) GetInstallationMetadata(_ context.Context, _ *api.GetInstallationMetadataRequest) (*api.GetInstallationMetadataResponse, error) { + perMachine, err := isPerMachineInstall() + if err != nil { + return nil, trace.Wrap(err) + } + + return &api.GetInstallationMetadataResponse{IsPerMachineInstall: perMachine}, nil +} + +func isPerMachineInstall() (bool, error) { + perMachineLocation, err := readPerMachineInstallLocation() + if err != nil { + if trace.IsNotFound(err) { + return false, nil + } + return false, trace.Wrap(err) + } + + exePath, err := os.Executable() + if err != nil { + return false, trace.Wrap(err) + } + + // tsh is placed in /resources/bin/tsh.exe. + exePathImperMachineLocation := filepath.Join(perMachineLocation, "resources", "bin", "tsh.exe") + + return exePath == exePathImperMachineLocation, nil +} + +func readPerMachineInstallLocation() (path string, err error) { + key, err := registry.OpenKey(registry.LOCAL_MACHINE, teleportConnectKeyPath, registry.READ) + if err != nil { + if errors.Is(err, registry.ErrNotExist) { + return "", trace.NotFound("registry key %s not found", teleportConnectKeyPath) + } + return "", trace.Wrap(err, "opening registry key %s", teleportConnectKeyPath) + } + + defer func() { + if closeErr := key.Close(); closeErr != nil && err == nil { + err = trace.Wrap(closeErr, "closing registry key %s", teleportConnectKeyPath) + } + }() + + path, _, err = key.GetStringValue(installLocationKey) + if err != nil { + return "", trace.Wrap(err, "reading registry value %s from %s", installLocationKey, teleportConnectKeyPath) + } + + return path, nil +} diff --git a/lib/teleterm/vnet/service_windows.go b/lib/teleterm/vnet/service_windows.go index d41fe2ee00f7d..5d5d29936b190 100644 --- a/lib/teleterm/vnet/service_windows.go +++ b/lib/teleterm/vnet/service_windows.go @@ -18,9 +18,13 @@ package vnet import ( "context" + "errors" "github.com/gravitational/trace" + "golang.org/x/sys/windows" + api "github.com/gravitational/teleport/gen/proto/go/teleport/lib/teleterm/vnet/v1" + "github.com/gravitational/teleport/lib/vnet" "github.com/gravitational/teleport/lib/vnet/diag" ) @@ -46,3 +50,25 @@ func (s *Service) platformDiagChecks(ctx context.Context) ([]diag.DiagCheck, err sshDiag, }, nil } + +// CheckInstallTimeRequirements verifies the existence of the VNet system service, which is installed only in per-machine setups. +func (s *Service) CheckInstallTimeRequirements(_ context.Context, _ *api.CheckInstallTimeRequirementsRequest) (*api.CheckInstallTimeRequirementsResponse, error) { + err := vnet.VerifyServiceInstalled() + if err == nil { + return &api.CheckInstallTimeRequirementsResponse{ + Status: &api.CheckInstallTimeRequirementsResponse_WindowsServiceStatus{ + WindowsServiceStatus: api.WindowsServiceStatus_WINDOWS_SERVICE_STATUS_OK, + }, + }, nil + } + + if errors.Is(err, windows.ERROR_SERVICE_DOES_NOT_EXIST) { + return &api.CheckInstallTimeRequirementsResponse{ + Status: &api.CheckInstallTimeRequirementsResponse_WindowsServiceStatus{ + WindowsServiceStatus: api.WindowsServiceStatus_WINDOWS_SERVICE_STATUS_DOES_NOT_EXIST, + }, + }, nil + + } + return nil, trace.Wrap(err) +} diff --git a/lib/vnet/service_windows.go b/lib/vnet/service_windows.go index 9823ef6279ab7..3c4d27faeacb9 100644 --- a/lib/vnet/service_windows.go +++ b/lib/vnet/service_windows.go @@ -157,3 +157,19 @@ func (w *handler) Execute(ctx context.Context, args []string) error { } return nil } + +// VerifyServiceInstalled returns nil if the service is installed and an error otherwise. +func VerifyServiceInstalled() error { + // Avoid [mgr.Connect] because it requests elevated permissions. + scManager, err := windows.OpenSCManager(nil /*machine*/, nil /*database*/, windows.SC_MANAGER_CONNECT) + if err != nil { + return trace.Wrap(err, "opening Windows service manager") + } + defer windows.CloseServiceHandle(scManager) + serviceNamePtr, err := syscall.UTF16PtrFromString(serviceName) + if err != nil { + return trace.Wrap(err, "converting service name to UTF16") + } + _, err = windows.OpenService(scManager, serviceNamePtr, serviceAccessFlags) + return trace.Wrap(err, "opening Windows service %v", serviceName) +} diff --git a/lib/windowsservice/install_windows.go b/lib/windowsservice/install_windows.go index 546791d1b229d..e57c5c5e65ec8 100644 --- a/lib/windowsservice/install_windows.go +++ b/lib/windowsservice/install_windows.go @@ -210,9 +210,9 @@ func assertTshInProgramFiles(tshPath string) error { if err := assertRegularFile(tshPath); err != nil { return trace.Wrap(err) } - programFiles := os.Getenv("PROGRAMFILES") - if programFiles == "" { - return trace.Errorf("PROGRAMFILES env var is not set") + programFiles, err := windows.KnownFolderPath(windows.FOLDERID_ProgramFiles, 0) + if err != nil { + return trace.Wrap(err, "failed to read Program Files path") } // Windows file paths are case-insensitive. cleanedProgramFiles := strings.ToLower(filepath.Clean(programFiles)) + string(filepath.Separator) diff --git a/proto/teleport/lib/teleterm/auto_update/v1/auto_update_service.proto b/proto/teleport/lib/teleterm/auto_update/v1/auto_update_service.proto index 74f21508339a7..9c4af6a657338 100644 --- a/proto/teleport/lib/teleterm/auto_update/v1/auto_update_service.proto +++ b/proto/teleport/lib/teleterm/auto_update/v1/auto_update_service.proto @@ -28,6 +28,9 @@ service AutoUpdateService { // Can be overridden with TELEPORT_CDN_BASE_URL env var. // OSS builds require this env var to be set, otherwise an error is returned. rpc GetDownloadBaseUrl(GetDownloadBaseUrlRequest) returns (GetDownloadBaseUrlResponse); + // GetInstallationMetadata returns installation metadata of the currently running app instance. + // Implemented only on Windows. + rpc GetInstallationMetadata(GetInstallationMetadataRequest) returns (GetInstallationMetadataResponse); } // Request for GetClusterVersions. @@ -66,3 +69,12 @@ message GetDownloadBaseUrlRequest {} message GetDownloadBaseUrlResponse { string base_url = 1; } + +// Request for GetInstallationMetadata. +message GetInstallationMetadataRequest {} + +// Response for GetInstallationMetadata. +message GetInstallationMetadataResponse { + // Determines whether updates should target a per-machine installation. + bool is_per_machine_install = 1; +} diff --git a/proto/teleport/lib/teleterm/vnet/v1/vnet_service.proto b/proto/teleport/lib/teleterm/vnet/v1/vnet_service.proto index 0336e122b523a..24ad548758c73 100644 --- a/proto/teleport/lib/teleterm/vnet/v1/vnet_service.proto +++ b/proto/teleport/lib/teleterm/vnet/v1/vnet_service.proto @@ -37,6 +37,10 @@ service VnetService { // VNet daemon. macOS only. tsh must be compiled with the vnetdaemon build tag. rpc GetBackgroundItemStatus(GetBackgroundItemStatusRequest) returns (GetBackgroundItemStatusResponse); + // CheckInstallTimeRequirements validates install-time prerequisites (for example, VNet service presence) that can + // only be changed by reinstalling the app. + rpc CheckInstallTimeRequirements(CheckInstallTimeRequirementsRequest) returns (CheckInstallTimeRequirementsResponse); + // RunDiagnostics runs a set of heuristics to determine if VNet actually works on the device, that // is receives network traffic and DNS queries. RunDiagnostics requires VNet to be started. rpc RunDiagnostics(RunDiagnosticsRequest) returns (RunDiagnosticsResponse); @@ -97,6 +101,23 @@ enum BackgroundItemStatus { BACKGROUND_ITEM_STATUS_NOT_SUPPORTED = 5; } +// WindowsServiceStatus maps to service-related errors in golang.org/x/sys/windows/zerrors_windows.go. +enum WindowsServiceStatus { + WINDOWS_SERVICE_STATUS_UNSPECIFIED = 0; + WINDOWS_SERVICE_STATUS_OK = 1; + WINDOWS_SERVICE_STATUS_DOES_NOT_EXIST = 2; +} + +// Request for CheckInstallTimeRequirementsRequest. +message CheckInstallTimeRequirementsRequest {} + +// Response for CheckInstallTimeRequirementsResponse. +message CheckInstallTimeRequirementsResponse { + oneof status { + WindowsServiceStatus windows_service_status = 1; + } +} + // Request for RunDiagnostics. message RunDiagnosticsRequest {} diff --git a/web/packages/teleterm/build_resources/installer.nsh b/web/packages/teleterm/build_resources/installer.nsh index 7a1c33e159365..61b0992186a0d 100644 --- a/web/packages/teleterm/build_resources/installer.nsh +++ b/web/packages/teleterm/build_resources/installer.nsh @@ -1,41 +1,69 @@ -# https://github.com/electron-userland/electron-builder/blob/v24.0.0-alpha.5/docs/configuration/nsis.md#custom-nsis-script - -# electron-builder adds `BUILD_RESOURCES_DIR\x86-unicode` as a plugin dir. -# But that dir name isn't very descriptive, so we add a custom plugin dir. -!addplugindir "${BUILD_RESOURCES_DIR}\nsis-plugins" - -# The EnVar plugin is recommended for env var modification as EnvVarUpdate doesn't handle long -# strings very well. -# https://nsis.sourceforge.io/Environmental_Variables:_append,_prepend,_and_remove_entries -# https://nsis.sourceforge.io/EnVar_plug-in - -!macro customInstall - # Make EnVar define system env vars since Connect is installed per-machine. - EnVar::SetHKLM - EnVar::AddValue "Path" $INSTDIR\resources\bin - - nsExec::ExecToStack '"$INSTDIR\resources\bin\tsh.exe" vnet-install-service' - Pop $0 # ExitCode - Pop $1 # Output - ${If} $0 != 0 - MessageBox MB_ICONSTOP \ - "tsh.exe vnet-install-service failed with exit code $0. Output: $1" - Quit - ${Endif} -!macroend - -!macro customUnInstall - EnVar::SetHKLM - # Inside the uninstaller, $INSTDIR is the directory where the uninstaller lies. - # Fortunately, electron-builder puts the uninstaller directly into the actual installation dir. - # https://nsis.sourceforge.io/Docs/Chapter4.html#varother - EnVar::DeleteValue "Path" $INSTDIR\resources\bin - - nsExec::ExecToStack '"$INSTDIR\resources\bin\tsh.exe" vnet-uninstall-service' - Pop $0 # ExitCode - Pop $1 # Output - ${If} $0 != 0 - MessageBox MB_ICONSTOP \ - "tsh.exe vnet-uninstall-service failed with exit code $0. The uninstaller is going to continue. Output: $1" - ${Endif} -!macroend +# https://github.com/electron-userland/electron-builder/blob/v24.0.0-alpha.5/docs/configuration/nsis.md#custom-nsis-script + +# electron-builder adds `BUILD_RESOURCES_DIR\x86-unicode` as a plugin dir. +# But that dir name isn't very descriptive, so we add a custom plugin dir. +!addplugindir "${BUILD_RESOURCES_DIR}\nsis-plugins" + +# The EnVar plugin is recommended for env var modification as EnvVarUpdate doesn't handle long +# strings very well. +# https://nsis.sourceforge.io/Environmental_Variables:_append,_prepend,_and_remove_entries +# https://nsis.sourceforge.io/EnVar_plug-in + +# To inform the user that VNet is available only in the per-machine mode, we need to display a message in the wizard. +# It could be added on a separate welcome page (which can be fully customized), but that would introduce an +# additional step in the wizard and create unnecessary friction. +# Instead, we modify the existing "selectUserMode" and "forAll" strings by hand (electron-builder doesn't allow customizing the translations from +# https://github.com/electron-userland/electron-builder/blob/6c20eeb1cf9fd10980cde3c9ce0602fa6b7c6972/packages/app-builder-lib/templates/nsis/assistedMessages.yml). +# Important: the message can't be too long, the template was designed for around two lines of text, the rest is clipped. +# +# Because of electron-builder's default setting which treats warnings as errors, we need to disable warnings 6030 +# (warning 6030: LangString "selectUserMode" set multiple times for 1033, wasting space). + +!pragma warning disable 6030 +!macro customHeader + LangString selectUserMode ${LANG_ENGLISH} "Select installation mode. Only the 'Anyone who uses this computer' option comes with VNet, Teleport's VPN-like experience for accessing TCP applications and SSH servers." + LangString forAll ${LANG_ENGLISH} "Anyone who uses this computer (&all users). Includes VNet support." +!macroend + +!macro customInstall + ${If} $installMode == "all" + # Make EnVar define system env vars when the app is installed per-machine. + EnVar::SetHKLM + EnVar::AddValue "Path" $INSTDIR\resources\bin + + nsExec::ExecToStack '"$INSTDIR\resources\bin\tsh.exe" vnet-install-service' + Pop $0 # ExitCode + Pop $1 # Output + ${If} $0 != 0 + MessageBox MB_ICONSTOP \ + "tsh.exe vnet-install-service failed with exit code $0. The installer is going to continue. Output: $1" + ${Endif} + + ${Else} + # Make EnVar define system user vars when the app is installed per-user. + EnVar::SetHKCU + EnVar::AddValue "Path" "$INSTDIR\resources\bin" + + ${EndIf} +!macroend + +!macro customUnInstall + ${If} $installMode == "all" + EnVar::SetHKLM + # Inside the uninstaller, $INSTDIR is the directory where the uninstaller lies. + # Fortunately, electron-builder puts the uninstaller directly into the actual installation dir. + # https://nsis.sourceforge.io/Docs/Chapter4.html#varother + EnVar::DeleteValue "Path" $INSTDIR\resources\bin + + nsExec::ExecToStack '"$INSTDIR\resources\bin\tsh.exe" vnet-uninstall-service' + Pop $0 # ExitCode + Pop $1 # Output + ${If} $0 != 0 + MessageBox MB_ICONSTOP \ + "tsh.exe vnet-uninstall-service failed with exit code $0. The uninstaller is going to continue. Output: $1" + ${Endif} + ${Else} + EnVar::SetHKCU + EnVar::DeleteValue "Path" "$INSTDIR\resources\bin" + ${EndIf} +!macroend diff --git a/web/packages/teleterm/electron-builder-config.js b/web/packages/teleterm/electron-builder-config.js index 0c27a11565670..f52f8fb198db2 100644 --- a/web/packages/teleterm/electron-builder-config.js +++ b/web/packages/teleterm/electron-builder-config.js @@ -211,6 +211,7 @@ module.exports = { extraResources: [ env.CONNECT_TSH_BIN_PATH && { from: env.CONNECT_TSH_BIN_PATH, + // Keep in sync with lib/teleterm/autoupdate/per_machine_windows.go. to: './bin/tsh.exe', }, env.CONNECT_WINTUN_DLL_PATH && { @@ -226,13 +227,20 @@ module.exports = { ].filter(Boolean), }, nsis: { + // Static app guid, calculated from appId and electron-builder's UUID. + guid: '22539266-67e8-54a3-83b9-dfdca7b33ee1', // Turn off blockmaps since we don't support automatic updates. // https://github.com/electron-userland/electron-builder/issues/2900#issuecomment-730571696 differentialPackage: false, - // Use a per-machine installation to support VNet. - // VNet installs a Windows service per-machine, and tsh.exe must be - // installed in a path that is not user-writable. - perMachine: true, + // Per-machine and per-user modes differ in features. + // VNet is available only in per-machine mode. + perMachine: false, + oneClick: false, + selectPerMachineByDefault: true, + // In installer.nsh, the `selectUserMode` message is overridden to display information + // about VNet availability. The message is only in English, so the multi-language + // installer should be disabled to avoid mixing languages in the installation wizard. + multiLanguageInstaller: false, }, rpm: { artifactName: '${name}-${version}.${arch}.${ext}', diff --git a/web/packages/teleterm/src/helpers.ts b/web/packages/teleterm/src/helpers.ts index eeecd5e83b635..447a2378673ca 100644 --- a/web/packages/teleterm/src/helpers.ts +++ b/web/packages/teleterm/src/helpers.ts @@ -23,6 +23,10 @@ import { Server } from 'gen-proto-ts/teleport/lib/teleterm/v1/server_pb'; import { PaginatedResource } from 'gen-proto-ts/teleport/lib/teleterm/v1/service_pb'; import * as api from 'gen-proto-ts/teleport/lib/teleterm/v1/tshd_events_service_pb'; import { WindowsDesktop } from 'gen-proto-ts/teleport/lib/teleterm/v1/windows_desktop_pb'; +import { + CheckInstallTimeRequirementsResponse, + WindowsServiceStatus, +} from 'gen-proto-ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb'; import { CheckReport, RouteConflictReport, @@ -204,3 +208,12 @@ export function reportOneOfIsSSHConfigurationReport( } { return report.oneofKind === 'sshConfigurationReport'; } + +export function statusOneOfIsWindowsServiceStatus( + status: CheckInstallTimeRequirementsResponse['status'] +): status is { + oneofKind: 'windowsServiceStatus'; + windowsServiceStatus: WindowsServiceStatus; +} { + return status.oneofKind === 'windowsServiceStatus'; +} diff --git a/web/packages/teleterm/src/mainProcess/mainProcess.ts b/web/packages/teleterm/src/mainProcess/mainProcess.ts index 21cd4177e1e41..9f31e452e1ff0 100644 --- a/web/packages/teleterm/src/mainProcess/mainProcess.ts +++ b/web/packages/teleterm/src/mainProcess/mainProcess.ts @@ -193,22 +193,27 @@ export default class MainProcess { this.initResolvingChildProcessAddressesAndTshdClients(); this.initIpc(); - const getClusterVersions = async () => { - const { autoUpdateService } = await this.tshdClients; - const { response } = await autoUpdateService.getClusterVersions({}); - return response; - }; - const getDownloadBaseUrl = async () => { - const { autoUpdateService } = await this.tshdClients; - const { - response: { baseUrl }, - } = await autoUpdateService.getDownloadBaseUrl({}); - return baseUrl; - }; this.appUpdater = new AppUpdater( makeAppUpdaterStorage(this.appStateFileStorage), - getClusterVersions, - getDownloadBaseUrl, + { + getDownloadBaseUrl: async () => { + const { autoUpdateService } = await this.tshdClients; + const { response } = await autoUpdateService.getDownloadBaseUrl({}); + return response.baseUrl; + }, + getClusterVersions: async () => { + const { autoUpdateService } = await this.tshdClients; + const { response } = await autoUpdateService.getClusterVersions({}); + return response; + }, + getInstallationMetadata: async () => { + const { autoUpdateService } = await this.tshdClients; + const { response } = await autoUpdateService.getInstallationMetadata( + {} + ); + return response; + }, + }, event => { if (event.kind === 'error') { event.error = serializeError(event.error); diff --git a/web/packages/teleterm/src/services/appUpdater/appUpdater.test.ts b/web/packages/teleterm/src/services/appUpdater/appUpdater.test.ts index 0c88bd45cbb55..aef61f35b91b1 100644 --- a/web/packages/teleterm/src/services/appUpdater/appUpdater.test.ts +++ b/web/packages/teleterm/src/services/appUpdater/appUpdater.test.ts @@ -102,10 +102,6 @@ function setUpAppUpdater(options: { storage?: AppUpdaterStorage; processEnvVar?: string; }) { - const clusterGetter = async () => { - return options.clusters; - }; - const nativeUpdater = new MockedMacUpdater(); const checkForUpdatesSpy = jest.spyOn(nativeUpdater, 'checkForUpdates'); @@ -113,8 +109,11 @@ function setUpAppUpdater(options: { let lastEvent: { value?: AppUpdateEvent } = {}; const appUpdater = new AppUpdater( options.storage || makeUpdaterStorage(), - clusterGetter, - async () => 'https://cdn.teleport.dev', + { + getClusterVersions: async () => options.clusters, + getDownloadBaseUrl: async () => 'https://cdn.teleport.dev', + getInstallationMetadata: async () => ({ isPerMachineInstall: false }), + }, event => { lastEvent.value = event; }, diff --git a/web/packages/teleterm/src/services/appUpdater/appUpdater.ts b/web/packages/teleterm/src/services/appUpdater/appUpdater.ts index 3962861483c4a..6c7796286dcc4 100644 --- a/web/packages/teleterm/src/services/appUpdater/appUpdater.ts +++ b/web/packages/teleterm/src/services/appUpdater/appUpdater.ts @@ -27,18 +27,21 @@ import { UpdateInfo as ElectronUpdateInfo, MacUpdater, AppUpdater as NativeUpdater, - NsisUpdater, ProgressInfo, RpmUpdater, UpdateCheckResult, } from 'electron-updater'; import { ProviderRuntimeOptions } from 'electron-updater/out/providers/Provider'; -import type { GetClusterVersionsResponse } from 'gen-proto-ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb'; +import type { + GetClusterVersionsResponse, + GetInstallationMetadataResponse, +} from 'gen-proto-ts/teleport/lib/teleterm/auto_update/v1/auto_update_service_pb'; import { AbortError } from 'shared/utils/error'; import { compare } from 'shared/utils/semVer'; import Logger from 'teleterm/logger'; +import { isTshdRpcError } from 'teleterm/services/tshd'; import { RootClusterUri } from 'teleterm/ui/uri'; import { @@ -51,6 +54,7 @@ import { ClientToolsUpdateProvider, ClientToolsVersionGetter, } from './clientToolsUpdateProvider'; +import { NsisDualModeUpdater } from './nsisDualModeUpdater'; export const TELEPORT_TOOLS_VERSION_ENV_VAR = 'TELEPORT_TOOLS_VERSION'; @@ -66,8 +70,11 @@ export class AppUpdater { constructor( private readonly storage: AppUpdaterStorage, - private readonly getClusterVersions: () => Promise, - readonly getDownloadBaseUrl: () => Promise, + private readonly client: { + getClusterVersions(): Promise; + getDownloadBaseUrl(): Promise; + getInstallationMetadata(): Promise; + }, private readonly emit: (event: AppUpdateEvent) => void, private versionEnvVar: string, /** Allows overring autoUpdater in tests. */ @@ -77,13 +84,28 @@ export class AppUpdater { await this.refreshAutoUpdatesStatus(); if (this.autoUpdatesStatus.enabled) { + const [baseUrl, installationMetadata] = await Promise.all([ + client.getDownloadBaseUrl(), + client.getInstallationMetadata().catch(error => { + if (isTshdRpcError(error, 'UNIMPLEMENTED')) { + return { isPerMachineInstall: false }; + } + throw error; + }), + ]); + return { version: this.autoUpdatesStatus.version, - baseUrl: await getDownloadBaseUrl(), + baseUrl, + isPerMachineInstall: installationMetadata.isPerMachineInstall, }; } }; + if (process.platform === 'win32') { + this.nativeUpdater = new NsisDualModeUpdater(); + } + this.nativeUpdater.setFeedURL({ provider: 'custom', // Wraps ClientToolsUpdateProvider to allow passing getClientToolsVersion. @@ -135,7 +157,7 @@ export class AppUpdater { supportsUpdates(): boolean { return ( this.nativeUpdater instanceof MacUpdater || - this.nativeUpdater instanceof NsisUpdater || + this.nativeUpdater instanceof NsisDualModeUpdater || this.nativeUpdater instanceof DebUpdater || this.nativeUpdater instanceof RpmUpdater ); @@ -330,7 +352,7 @@ export class AppUpdater { this.autoUpdatesStatus = await resolveAutoUpdatesStatus({ versionEnvVar: this.versionEnvVar, managingClusterUri, - getClusterVersions: this.getClusterVersions, + getClusterVersions: this.client.getClusterVersions, }); this.logger.info('Resolved auto updates status', this.autoUpdatesStatus); } diff --git a/web/packages/teleterm/src/services/appUpdater/clientToolsUpdateProvider.ts b/web/packages/teleterm/src/services/appUpdater/clientToolsUpdateProvider.ts index 247ac117c06b7..9796bd5508966 100644 --- a/web/packages/teleterm/src/services/appUpdater/clientToolsUpdateProvider.ts +++ b/web/packages/teleterm/src/services/appUpdater/clientToolsUpdateProvider.ts @@ -21,7 +21,6 @@ import { AppUpdater, DebUpdater, MacUpdater, - NsisUpdater, Provider, ResolvedUpdateFileInfo, RpmUpdater, @@ -32,6 +31,7 @@ import { ProviderRuntimeOptions } from 'electron-updater/out/providers/Provider' import { compare, major } from 'shared/utils/semVer'; import { UnsupportedVersionError } from './errors'; +import { NsisDualModeUpdater } from './nsisDualModeUpdater'; const CHECKSUM_FETCH_TIMEOUT = 5_000; // Example: 99a2fe26681073de56de4229dd9cd6655fef22759579b7b9bc359e018ea1007099a2fe26681073de56de4229dd9cd6655fef22759579b7b9bc359e018ea10070 Teleport Connect-17.5.4-mac.zip @@ -71,8 +71,11 @@ export class ClientToolsUpdateProvider extends Provider { }; } - const { baseUrl, version } = clientTools; - const updatesSupport = areManagedUpdatesSupportedInConnect(version); + const { baseUrl, version, isPerMachineInstall } = clientTools; + const updatesSupport = areManagedUpdatesSupportedInConnect( + this.nativeUpdater, + version + ); if (updatesSupport.supported === false) { throw new UnsupportedVersionError(version, updatesSupport.minVersion); } @@ -87,10 +90,11 @@ export class ClientToolsUpdateProvider extends Provider { sha512: '', files: [ { - // Effective only on Windows. - isAdminRightsRequired: true, url: fileUrl, sha512, + // Taken into account only on Windows. + // Read in NsisDualModeUpdater. + isAdminRightsRequired: isPerMachineInstall, }, ], }; @@ -116,6 +120,11 @@ export type ClientToolsVersionGetter = () => Promise< baseUrl: string; /** Version to download. */ version: string; + /** + * Determines whether updates should target a per-machine installation. + * Applicable only on Windows. + */ + isPerMachineInstall: boolean; } | undefined >; @@ -124,7 +133,7 @@ function makeDownloadFilename(updater: AppUpdater, version: string): string { if (updater instanceof MacUpdater) { return `Teleport Connect-${version}-mac.zip`; } - if (updater instanceof NsisUpdater) { + if (updater instanceof NsisDualModeUpdater) { return `Teleport Connect Setup-${version}.exe`; } if (updater instanceof RpmUpdater) { @@ -158,6 +167,7 @@ async function fetchChecksum(fileUrl: string): Promise { // TODO(gzdunek) DELETE IN v20.0.0 function areManagedUpdatesSupportedInConnect( + updater: AppUpdater, version: string ): { supported: true } | { supported: false; minVersion: string } { const thresholds = { @@ -165,6 +175,12 @@ function areManagedUpdatesSupportedInConnect( 17: '17.7.3', }; + if (updater instanceof NsisDualModeUpdater) { + // TODO(gzdunek): Update the thresholds to disallow downgrades + // to versions that don't support the per-user mode or don't + // install per-machine updates through the update service. + } + const majorVersion = major(version); if (majorVersion >= 19) { return { supported: true }; diff --git a/web/packages/teleterm/src/services/appUpdater/nsisDualModeUpdater.ts b/web/packages/teleterm/src/services/appUpdater/nsisDualModeUpdater.ts new file mode 100644 index 0000000000000..47820d84bba22 --- /dev/null +++ b/web/packages/teleterm/src/services/appUpdater/nsisDualModeUpdater.ts @@ -0,0 +1,121 @@ +/** + * Teleport + * Copyright (C) 2026 Gravitational, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import path from 'node:path'; + +import { shell } from 'electron'; +import { NsisUpdater } from 'electron-updater'; +import { InstallOptions } from 'electron-updater/out/BaseUpdater'; + +/** + * Extends the standard NSIS to ensure that a per-user installation won't attempt + * to update the per-machine one. + */ +export class NsisDualModeUpdater extends NsisUpdater { + constructor() { + super(); + } + + protected override doInstall(options: InstallOptions): boolean { + if (options.isAdminRightsRequired) { + // TODO(gzdunek): Call the privileged update service. + return super.doInstall(options); + } else { + return this.doInstallPerUser(options); + } + } + + /** + * Copied from `doInstall` in NSIS updater: + * https://github.com/electron-userland/electron-builder/blob/7b5901b77dfae417c29944656b80c583384de026/packages/electron-updater/src/NsisUpdater.ts#L126-L181 + * (commit 8ba9be481e3b777aa77884d265fd9b7f927a8a99). + * + * The only change is the addition of the `/currentuser` flag to prevent attempts + * to update an existing per-machine installation. + */ + protected doInstallPerUser(options: InstallOptions): boolean { + const installerPath = this.installerPath; + if (installerPath == null) { + this.dispatchError( + new Error("No update filepath provided, can't quit and install") + ); + return false; + } + + const args = ['--updated']; + + // Do not attempt to update the per-machine version if it exists. + args.push('/currentuser'); + + if (options.isSilent) { + args.push('/S'); + } + + if (options.isForceRunAfter) { + args.push('--force-run'); + } + + if (this.installDirectory) { + // maybe check if folder exists + args.push(`/D=${this.installDirectory}`); + } + + const packagePath = + this.downloadedUpdateHelper == null + ? null + : this.downloadedUpdateHelper.packageFile; + if (packagePath != null) { + // only = form is supported + args.push(`--package-file=${packagePath}`); + } + + const callUsingElevation = (): void => { + this.spawnLog( + path.join(process.resourcesPath, 'elevate.exe'), + [installerPath].concat(args) + ).catch(e => this.dispatchError(e)); + }; + + if (options.isAdminRightsRequired) { + this._logger.info( + 'isAdminRightsRequired is set to true, run installer using elevate.exe' + ); + callUsingElevation(); + return true; + } + + this.spawnLog(installerPath, args).catch((e: Error) => { + // https://github.com/electron-userland/electron-builder/issues/1129 + // Node 8 sends errors: https://nodejs.org/dist/latest-v8.x/docs/api/errors.html#errors_common_system_errors + const errorCode = (e as NodeJS.ErrnoException).code; + this._logger.info( + `Cannot run installer: error code: ${errorCode}, error message: "${e.message}", will be executed again using elevate if EACCES, and will try to use electron.shell.openItem if ENOENT` + ); + if (errorCode === 'UNKNOWN' || errorCode === 'EACCES') { + callUsingElevation(); + } else if (errorCode === 'ENOENT') { + shell + .openPath(installerPath) + .catch((err: Error) => this.dispatchError(err)); + } else { + this.dispatchError(e); + } + }); + return true; + } +} diff --git a/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts b/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts index 382f7bb109d2a..b9c1e62e030e8 100644 --- a/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts +++ b/web/packages/teleterm/src/services/tshd/fixtures/mocks.ts @@ -129,6 +129,8 @@ export class MockVnetClient implements VnetClient { '/Users/user/Library/Application Support/Teleport Connect/tsh/vnet_ssh_config', }); getBackgroundItemStatus = () => new MockedUnaryCall({ status: 0 }); + checkInstallTimeRequirements = () => + new MockedUnaryCall({ status: { oneofKind: undefined } }); runDiagnostics() { return new MockedUnaryCall({ report: { @@ -147,4 +149,6 @@ export class MockAutoUpdateClient implements AutoUpdateClient { unreachableClusters: [], }); getDownloadBaseUrl = () => new MockedUnaryCall({ baseUrl: '' }); + getInstallationMetadata = () => + new MockedUnaryCall({ isPerMachineInstall: false }); } diff --git a/web/packages/teleterm/src/services/tshd/testHelpers.ts b/web/packages/teleterm/src/services/tshd/testHelpers.ts index 43297e4465f54..4fa3e2d0a0c30 100644 --- a/web/packages/teleterm/src/services/tshd/testHelpers.ts +++ b/web/packages/teleterm/src/services/tshd/testHelpers.ts @@ -381,3 +381,16 @@ export const makeAuthSettings = ( clientVersionStatus: ClientVersionStatus.OK, ...props, }); + +export const makeTshdRpcError = ( + props: Partial = {} +): TshdRpcError => { + return { + name: 'TshdRpcError', + isResolvableWithRelogin: false, + code: 'UNKNOWN', + message: 'Error occurred', + toString: () => 'Error occurred', + ...props, + }; +}; diff --git a/web/packages/teleterm/src/ui/Search/SearchBar.test.tsx b/web/packages/teleterm/src/ui/Search/SearchBar.test.tsx index ed57077fdb8b4..b8e3c74b2be79 100644 --- a/web/packages/teleterm/src/ui/Search/SearchBar.test.tsx +++ b/web/packages/teleterm/src/ui/Search/SearchBar.test.tsx @@ -62,7 +62,7 @@ const displayResultsAction: SearchAction = { perform() {}, }; -it('does not display empty results copy after selecting two filters', () => { +it('does not display empty results copy after selecting two filters', async () => { const appContext = setUpContext('/clusters/foo'); const mockActionAttempts = { @@ -97,11 +97,11 @@ it('does not display empty results copy after selecting two filters', () => { ); - const results = screen.getByRole('menu'); + const results = await screen.findByRole('menu'); expect(results).not.toHaveTextContent('No matching results found'); }); -it('displays empty results copy after providing search query for which there is no results', () => { +it('displays empty results copy after providing search query for which there is no results', async () => { const appContext = setUpContext('/clusters/foo'); const mockActionAttempts = { @@ -131,11 +131,11 @@ it('displays empty results copy after providing search query for which there is ); - const results = screen.getByRole('menu'); + const results = await screen.findByRole('menu'); expect(results).toHaveTextContent('No matching results found.'); }); -it('includes offline cluster names in the empty results copy', () => { +it('includes offline cluster names in the empty results copy', async () => { const cluster = makeRootCluster({ connected: false }); const appContext = setUpContext(cluster.uri); appContext.clustersService.setState(draftState => { @@ -169,7 +169,7 @@ it('includes offline cluster names in the empty results copy', () => { ); - const results = screen.getByRole('menu'); + const results = await screen.findByRole('menu'); expect(results).toHaveTextContent('No matching results found.'); expect(results).toHaveTextContent( `The cluster ${cluster.name} was excluded from the search because you are not logged in to it.` @@ -217,7 +217,7 @@ it('notifies about resource search errors and allows to display details', async ); - const results = screen.getByRole('menu'); + const results = await screen.findByRole('menu'); expect(results).toHaveTextContent( 'Some of the search results are incomplete.' ); @@ -269,7 +269,8 @@ it('maintains focus on the search input after closing a resource search error mo ); - await act(() => user.type(screen.getByRole('searchbox'), 'foo')); + const searchbox = await screen.findByRole('searchbox'); + await act(() => user.type(searchbox, 'foo')); expect(screen.getByRole('menu')).toHaveTextContent( 'Some of the search results are incomplete.' @@ -333,7 +334,8 @@ it('shows a login modal when a request to a cluster from the current workspace f ); - await user.type(screen.getByRole('searchbox'), 'foo'); + const searchbox = await screen.findByRole('searchbox'); + await user.type(searchbox, 'foo'); // Verify that the login modal was shown after typing in the search box. await waitFor(() => { @@ -378,7 +380,8 @@ it('closes on a click on an unfocusable element outside of the search bar', asyn ); - await user.type(screen.getByRole('searchbox'), 'foo'); + const searchbox = await screen.findByRole('searchbox'); + await user.type(searchbox, 'foo'); expect(screen.getByRole('menu')).toBeInTheDocument(); await user.click(screen.getByTestId('unfocusable-element')); diff --git a/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.test.tsx b/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.test.tsx index 54f6b626b5d2d..5378fd58e7de5 100644 --- a/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.test.tsx +++ b/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.test.tsx @@ -42,6 +42,8 @@ describe('VnetSliderStepHeader', () => { ); + await screen.findByText('Start VNet'); + const listItem = screen.getByTitle('Go back to Connections'); const openDocumentationButton = screen.getByTitle( 'Open VNet documentation' @@ -84,6 +86,8 @@ describe('VnetSliderStepHeader', () => { ); + await screen.findByText('Start VNet'); + expect(document.body).toHaveFocus(); await user.tab(); diff --git a/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx b/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx index 9b59d2a654d4f..31d462562f00a 100644 --- a/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx +++ b/web/packages/teleterm/src/ui/Vnet/VnetConnectionItem.tsx @@ -114,6 +114,7 @@ const VnetConnectionItemBase = forwardRef< diagnosticsAttempt, getDisabledDiagnosticsReason, showDiagWarningIndicator, + installTimeRequirementsCheck, } = useVnetContext(); const { close: closeConnectionsPanel } = useConnectionsContext(); const rootClusterUri = useStoreSelector( @@ -122,7 +123,9 @@ const VnetConnectionItemBase = forwardRef< ); const isUserInWorkspace = !!rootClusterUri; const isProcessing = - startAttempt.status === 'processing' || stopAttempt.status === 'processing'; + startAttempt.status === 'processing' || + stopAttempt.status === 'processing' || + installTimeRequirementsCheck.status === 'unknown'; const disabledDiagnosticsReason = getDisabledDiagnosticsReason(diagnosticsAttempt); const indicatorStatus: ConnectionStatus = useMemo(() => { @@ -130,6 +133,7 @@ const VnetConnectionItemBase = forwardRef< if ( startAttempt.status === 'error' || stopAttempt.status === 'error' || + installTimeRequirementsCheck.status === 'failed' || (status.value === 'stopped' && status.reason.value === 'unexpected-shutdown') ) { @@ -145,7 +149,13 @@ const VnetConnectionItemBase = forwardRef< } return 'on'; - }, [startAttempt, stopAttempt, status, showDiagWarningIndicator]); + }, [ + startAttempt, + stopAttempt, + status, + showDiagWarningIndicator, + installTimeRequirementsCheck, + ]); const onEnterPress = (event: React.KeyboardEvent) => { if ( @@ -361,6 +371,7 @@ const VnetConnectionItemBase = forwardRef< key={toggleVnetButtonKey} size="small" width={toggleVnetButtonWidth} + disabled={installTimeRequirementsCheck.status === 'failed'} title="" onClick={e => { e.stopPropagation(); @@ -373,6 +384,7 @@ const VnetConnectionItemBase = forwardRef< { e.stopPropagation(); start(); diff --git a/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.story.tsx b/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.story.tsx index fa6abef75c780..b68be7e1dc137 100644 --- a/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.story.tsx +++ b/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.story.tsx @@ -19,6 +19,7 @@ import { Meta, StoryObj } from '@storybook/react-vite'; import { useEffect } from 'react'; import { Box } from 'design'; +import { WindowsServiceStatus } from 'gen-proto-ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb'; import { CheckAttemptStatus, CheckReportStatus, @@ -50,6 +51,7 @@ type StoryProps = { diagReport: 'ok' | 'issues-found' | 'failed-checks'; isWorkspacePresent: boolean; unexpectedShutdown: boolean; + windowsVNetServiceNotFound: boolean; }; const defaultArgs: StoryProps = { @@ -63,6 +65,7 @@ const defaultArgs: StoryProps = { diagReport: 'ok', isWorkspacePresent: true, unexpectedShutdown: false, + windowsVNetServiceNotFound: false, }; const meta: Meta = { @@ -109,6 +112,10 @@ const meta: Meta = { description: "If there's no workspace, the button to open the diag report is disabled.", }, + windowsVNetServiceNotFound: { + description: + 'When the app is installed in a per-user mode, the VNet service is not installed.', + }, }, render: props => , }; @@ -117,6 +124,16 @@ export default meta; function VnetSliderStep(props: StoryProps) { const appContext = new MockAppContext(); + if (props.windowsVNetServiceNotFound) { + appContext.vnet.checkInstallTimeRequirements = () => + new MockedUnaryCall({ + status: { + oneofKind: 'windowsServiceStatus' as const, + windowsServiceStatus: WindowsServiceStatus.DOES_NOT_EXIST, + }, + }); + } + if (props.isWorkspacePresent) { appContext.addRootCluster(makeRootCluster()); } @@ -308,3 +325,10 @@ export const SelfHostedWithManyLeavesAndZones: StoryObj = { ], }, }; + +export const WindowsServiceNotInstalled: StoryObj = { + args: { + ...defaultArgs, + windowsVNetServiceNotFound: true, + }, +}; diff --git a/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.tsx b/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.tsx index 49e4afd0a75ba..bd0d8cbfcef02 100644 --- a/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.tsx +++ b/web/packages/teleterm/src/ui/Vnet/VnetSliderStep.tsx @@ -43,6 +43,7 @@ export const VnetSliderStep = (props: StepComponentProps) => { status, startAttempt, stopAttempt, + installTimeRequirementsCheck, runDiagnostics, reinstateDiagnosticsAlert, } = useVnetContext(); @@ -88,6 +89,26 @@ export const VnetSliderStep = (props: StepComponentProps) => { } `} > + {installTimeRequirementsCheck.status === 'failed' && ( + <> + {installTimeRequirementsCheck.reason.kind === + 'missing-windows-service' && ( + + VNet system service is not installed.
+ To use VNet, reinstall Teleport Connect selecting 'Anyone + who uses this computer' option. Administrator privileges + will be required. +
+ )} + {installTimeRequirementsCheck.reason.kind === 'error' && ( + + Could not perform VNet installation requirements checks:{' '} + {installTimeRequirementsCheck.reason.statusText} + + )} + + )} + {startAttempt.status === 'error' && ( Could not start VNet: {startAttempt.statusText} )} diff --git a/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx b/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx index e08aecf3a2b21..7b51f48df98e8 100644 --- a/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx +++ b/web/packages/teleterm/src/ui/Vnet/vnetContext.test.tsx @@ -26,6 +26,8 @@ import { useImperativeHandle, } from 'react'; +import { WindowsServiceStatus } from 'gen-proto-ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb'; + import { MockedUnaryCall } from 'teleterm/services/tshd/cloneableClient'; import { makeRootCluster } from 'teleterm/services/tshd/testHelpers'; import { @@ -104,10 +106,48 @@ describe('autostart', () => { ...appContext.statePersistenceService.getState(), vnet: { autoStart: false, hasEverStarted: false }, }); + const { promise, resolve } = Promise.withResolvers(); + appContext.vnet.checkInstallTimeRequirements = async () => { + const response = new MockedUnaryCall({ + status: { + oneofKind: undefined, + }, + }); + resolve(response); + return response; + }; + const { result } = renderHook(() => useVnetContext(), { + wrapper: createWrapper(Wrapper, { appContext }), + }); + await act(() => promise); + expect(result.current.startAttempt.status).toEqual(''); + }); + + it('does not start VNet if Windows system service does not exist', async () => { + const appContext = new MockAppContext(); + appContext.workspacesService.setState(draft => { + draft.isInitialized = true; + }); + appContext.statePersistenceService.putState({ + ...appContext.statePersistenceService.getState(), + vnet: { autoStart: true, hasEverStarted: true }, + }); + const { promise, resolve } = Promise.withResolvers(); + appContext.vnet.checkInstallTimeRequirements = async () => { + const response = new MockedUnaryCall({ + status: { + oneofKind: 'windowsServiceStatus' as const, + windowsServiceStatus: WindowsServiceStatus.DOES_NOT_EXIST, + }, + }); + resolve(response); + return response; + }; const { result } = renderHook(() => useVnetContext(), { wrapper: createWrapper(Wrapper, { appContext }), }); + await act(() => promise); expect(result.current.startAttempt.status).toEqual(''); }); diff --git a/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx b/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx index d0130ddd155e8..f76d6565f89e0 100644 --- a/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx +++ b/web/packages/teleterm/src/ui/Vnet/vnetContext.tsx @@ -32,11 +32,14 @@ import { Action } from 'design/Alert'; import { BackgroundItemStatus, GetServiceInfoResponse, + WindowsServiceStatus, + type CheckInstallTimeRequirementsResponse, } from 'gen-proto-ts/teleport/lib/teleterm/vnet/v1/vnet_service_pb'; import { Report } from 'gen-proto-ts/teleport/lib/vnet/diag/v1/diag_pb'; import { useStateRef } from 'shared/hooks'; import { Attempt, makeEmptyAttempt, useAsync } from 'shared/hooks/useAsync'; +import { statusOneOfIsWindowsServiceStatus } from 'teleterm/helpers'; import { cloneAbortSignal, isTshdRpcError } from 'teleterm/services/tshd'; import { hasReportFoundIssues } from 'teleterm/services/vnet/diag'; import { useAppContext } from 'teleterm/ui/appContextProvider'; @@ -84,6 +87,11 @@ export type VnetContext = { getDisabledDiagnosticsReason: ( runDiagnosticsAttempt: Attempt ) => string; + /** + * Status of install-time prerequisites (for example, VNet service presence) that can + * only be changed by reinstalling the app. + */ + installTimeRequirementsCheck: InstallTimeRequirementsCheck; /** * Dismisses the diagnostics alert shown in the VNet panel. It won't be shown again until the user * reinstates the alert by manually requesting diagnostics to be run from the VNet panel. @@ -169,6 +177,22 @@ export const VnetContextProvider: FC< ); const isSupported = platform === 'darwin' || platform === 'win32'; + const [checkInstallTimeRequirementsAttempt, checkInstallTimeRequirements] = + useAsync( + useCallback(async () => { + const { response } = await vnet.checkInstallTimeRequirements({}); + return response; + }, [vnet]) + ); + const installTimeRequirementsCheck = useMemo( + () => makeInstallTimeRequirements(checkInstallTimeRequirementsAttempt), + [checkInstallTimeRequirementsAttempt] + ); + + useEffect(() => { + checkInstallTimeRequirements(); + }, [checkInstallTimeRequirements]); + const [startAttempt, start] = useAsync( useCallback(async () => { const { didBackgroundItemRequireEnablement } = @@ -339,7 +363,8 @@ export const VnetContextProvider: FC< // Accessing resources through VNet might trigger the MFA modal, // so we have to wait for the tshd events service to be initialized. isWorkspaceStateInitialized && - startAttempt.status === '' + startAttempt.status === '' && + installTimeRequirementsCheck.status === 'success' ) { const [, error] = await start(); @@ -352,7 +377,7 @@ export const VnetContextProvider: FC< }; handleAutoStart(); - }, [isWorkspaceStateInitialized]); + }, [isWorkspaceStateInitialized, installTimeRequirementsCheck.status]); useEffect( function handleUnexpectedShutdown() { @@ -533,6 +558,7 @@ export const VnetContextProvider: FC< showDiagWarningIndicator, hasEverStarted, openSSHConfigurationModal, + installTimeRequirementsCheck, }} > {children} @@ -584,3 +610,58 @@ const checkDaemonBackgroundItemStatus = async ( ); return { didBackgroundItemRequireEnablement: true }; }; + +function makeInstallTimeRequirements( + attempt: Attempt +): InstallTimeRequirementsCheck { + switch (attempt.status) { + case '': + case 'processing': + return { status: 'unknown' }; + case 'error': + if (isTshdRpcError(attempt.error, 'UNIMPLEMENTED')) { + return { status: 'success' }; + } + return { + status: 'failed', + reason: { + kind: 'error', + statusText: attempt.statusText, + }, + }; + } + + const { status } = attempt.data; + if ( + statusOneOfIsWindowsServiceStatus(status) && + status.windowsServiceStatus === WindowsServiceStatus.DOES_NOT_EXIST + ) { + return { + status: 'failed', + reason: { + kind: 'missing-windows-service', + }, + }; + } + + return { status: 'success' }; +} + +type InstallTimeRequirementsCheck = + | { + status: 'unknown'; + } + | { + status: 'success'; + } + | { + status: 'failed'; + reason: + | { + kind: 'missing-windows-service'; + } + | { + kind: 'error'; + statusText: string; + }; + };