diff --git a/api/client/dynamicwindows/dynamicwindows.go b/api/client/dynamicwindows/dynamicwindows.go index 32ba1762f1a..19d89e619fa 100644 --- a/api/client/dynamicwindows/dynamicwindows.go +++ b/api/client/dynamicwindows/dynamicwindows.go @@ -85,9 +85,25 @@ func (c *Client) UpdateDynamicWindowsDesktop(ctx context.Context, desktop types. } } +func (c *Client) UpsertDynamicWindowsDesktop(ctx context.Context, desktop types.DynamicWindowsDesktop) (types.DynamicWindowsDesktop, error) { + switch desktop := desktop.(type) { + case *types.DynamicWindowsDesktopV1: + desktop, err := c.grpcClient.UpsertDynamicWindowsDesktop(ctx, &dynamicwindows.UpsertDynamicWindowsDesktopRequest{ + Desktop: desktop, + }) + return desktop, trace.Wrap(err) + default: + return nil, trace.BadParameter("unknown desktop type: %T", desktop) + } +} + func (c *Client) DeleteDynamicWindowsDesktop(ctx context.Context, name string) error { _, err := c.grpcClient.DeleteDynamicWindowsDesktop(ctx, &dynamicwindows.DeleteDynamicWindowsDesktopRequest{ Name: name, }) return trace.Wrap(err) } + +func (c *Client) DeleteAllDynamicWindowsDesktops(ctx context.Context) error { + return trace.NotImplemented("DeleteAllDynamicWindowsDesktops is not supported in the gRPC client") +} diff --git a/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go b/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go index b09748485f1..3523ac1e79d 100644 --- a/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go +++ b/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service.pb.go @@ -200,7 +200,7 @@ func (x *GetDynamicWindowsDesktopRequest) GetName() string { return "" } -// CreateDynamicWindowsDesktopRequest is a request for a specific dynamic Windows desktop. +// CreateDynamicWindowsDesktopRequest is used for creating new dynamic Windows desktops. type CreateDynamicWindowsDesktopRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -247,7 +247,7 @@ func (x *CreateDynamicWindowsDesktopRequest) GetDesktop() *types.DynamicWindowsD return nil } -// UpdateDynamicWindowsDesktopRequest is a request for a specific dynamic Windows desktop. +// UpdateDynamicWindowsDesktopRequest is used for updating existing dynamic Windows desktops. type UpdateDynamicWindowsDesktopRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -294,6 +294,53 @@ func (x *UpdateDynamicWindowsDesktopRequest) GetDesktop() *types.DynamicWindowsD return nil } +// UpsertDynamicWindowsDesktopRequest is used for upserting dynamic Windows desktops. +type UpsertDynamicWindowsDesktopRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // desktop to be upserted + Desktop *types.DynamicWindowsDesktopV1 `protobuf:"bytes,1,opt,name=desktop,proto3" json:"desktop,omitempty"` +} + +func (x *UpsertDynamicWindowsDesktopRequest) Reset() { + *x = UpsertDynamicWindowsDesktopRequest{} + mi := &file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpsertDynamicWindowsDesktopRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpsertDynamicWindowsDesktopRequest) ProtoMessage() {} + +func (x *UpsertDynamicWindowsDesktopRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes[5] + 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 UpsertDynamicWindowsDesktopRequest.ProtoReflect.Descriptor instead. +func (*UpsertDynamicWindowsDesktopRequest) Descriptor() ([]byte, []int) { + return file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDescGZIP(), []int{5} +} + +func (x *UpsertDynamicWindowsDesktopRequest) GetDesktop() *types.DynamicWindowsDesktopV1 { + if x != nil { + return x.Desktop + } + return nil +} + // DeleteDynamicWindowsDesktopRequest is a request to delete a Windows desktop host. type DeleteDynamicWindowsDesktopRequest struct { state protoimpl.MessageState @@ -306,7 +353,7 @@ type DeleteDynamicWindowsDesktopRequest struct { func (x *DeleteDynamicWindowsDesktopRequest) Reset() { *x = DeleteDynamicWindowsDesktopRequest{} - mi := &file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes[5] + mi := &file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -318,7 +365,7 @@ func (x *DeleteDynamicWindowsDesktopRequest) String() string { func (*DeleteDynamicWindowsDesktopRequest) ProtoMessage() {} func (x *DeleteDynamicWindowsDesktopRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes[5] + mi := &file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -331,7 +378,7 @@ func (x *DeleteDynamicWindowsDesktopRequest) ProtoReflect() protoreflect.Message // Deprecated: Use DeleteDynamicWindowsDesktopRequest.ProtoReflect.Descriptor instead. func (*DeleteDynamicWindowsDesktopRequest) Descriptor() ([]byte, []int) { - return file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDescGZIP(), []int{5} + return file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDescGZIP(), []int{6} } func (x *DeleteDynamicWindowsDesktopRequest) GetName() string { @@ -383,11 +430,17 @@ var file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDesc = []byt 0x0a, 0x07, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x56, 0x31, 0x52, + 0x07, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x22, 0x5e, 0x0a, 0x22, 0x55, 0x70, 0x73, 0x65, + 0x72, 0x74, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, + 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, + 0x0a, 0x07, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, + 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x56, 0x31, 0x52, 0x07, 0x64, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x22, 0x38, 0x0a, 0x22, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x32, 0xa3, 0x05, 0x0a, 0x15, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, + 0x6d, 0x65, 0x32, 0xa2, 0x06, 0x0a, 0x15, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x9b, 0x01, 0x0a, 0x1a, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x73, 0x12, 0x3d, 0x2e, 0x74, 0x65, @@ -422,21 +475,29 @@ var file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDesc = []byt 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x56, - 0x31, 0x12, 0x75, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x79, 0x6e, 0x61, 0x6d, + 0x31, 0x12, 0x7d, 0x0a, 0x1b, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x12, 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x79, 0x6e, 0x61, - 0x6d, 0x69, 0x63, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, + 0x6d, 0x69, 0x63, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, + 0x73, 0x65, 0x72, 0x74, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x60, 0x5a, 0x5e, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, - 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, - 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x77, - 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, - 0x63, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x1a, 0x1e, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x56, 0x31, + 0x12, 0x75, 0x0a, 0x1b, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, + 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x12, + 0x3e, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x79, 0x6e, 0x61, 0x6d, + 0x69, 0x63, 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x57, 0x69, 0x6e, 0x64, 0x6f, 0x77, + 0x73, 0x44, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x60, 0x5a, 0x5e, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x61, 0x70, 0x69, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x2f, 0x74, 0x65, + 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, 0x77, 0x69, + 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x63, + 0x77, 0x69, 0x6e, 0x64, 0x6f, 0x77, 0x73, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -451,36 +512,40 @@ func file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDescGZIP() return file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDescData } -var file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_goTypes = []any{ (*ListDynamicWindowsDesktopsRequest)(nil), // 0: teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsRequest (*ListDynamicWindowsDesktopsResponse)(nil), // 1: teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsResponse (*GetDynamicWindowsDesktopRequest)(nil), // 2: teleport.dynamicwindows.v1.GetDynamicWindowsDesktopRequest (*CreateDynamicWindowsDesktopRequest)(nil), // 3: teleport.dynamicwindows.v1.CreateDynamicWindowsDesktopRequest (*UpdateDynamicWindowsDesktopRequest)(nil), // 4: teleport.dynamicwindows.v1.UpdateDynamicWindowsDesktopRequest - (*DeleteDynamicWindowsDesktopRequest)(nil), // 5: teleport.dynamicwindows.v1.DeleteDynamicWindowsDesktopRequest - (*types.DynamicWindowsDesktopV1)(nil), // 6: types.DynamicWindowsDesktopV1 - (*emptypb.Empty)(nil), // 7: google.protobuf.Empty + (*UpsertDynamicWindowsDesktopRequest)(nil), // 5: teleport.dynamicwindows.v1.UpsertDynamicWindowsDesktopRequest + (*DeleteDynamicWindowsDesktopRequest)(nil), // 6: teleport.dynamicwindows.v1.DeleteDynamicWindowsDesktopRequest + (*types.DynamicWindowsDesktopV1)(nil), // 7: types.DynamicWindowsDesktopV1 + (*emptypb.Empty)(nil), // 8: google.protobuf.Empty } var file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_depIdxs = []int32{ - 6, // 0: teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsResponse.desktops:type_name -> types.DynamicWindowsDesktopV1 - 6, // 1: teleport.dynamicwindows.v1.CreateDynamicWindowsDesktopRequest.desktop:type_name -> types.DynamicWindowsDesktopV1 - 6, // 2: teleport.dynamicwindows.v1.UpdateDynamicWindowsDesktopRequest.desktop:type_name -> types.DynamicWindowsDesktopV1 - 0, // 3: teleport.dynamicwindows.v1.DynamicWindowsService.ListDynamicWindowsDesktops:input_type -> teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsRequest - 2, // 4: teleport.dynamicwindows.v1.DynamicWindowsService.GetDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.GetDynamicWindowsDesktopRequest - 3, // 5: teleport.dynamicwindows.v1.DynamicWindowsService.CreateDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.CreateDynamicWindowsDesktopRequest - 4, // 6: teleport.dynamicwindows.v1.DynamicWindowsService.UpdateDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.UpdateDynamicWindowsDesktopRequest - 5, // 7: teleport.dynamicwindows.v1.DynamicWindowsService.DeleteDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.DeleteDynamicWindowsDesktopRequest - 1, // 8: teleport.dynamicwindows.v1.DynamicWindowsService.ListDynamicWindowsDesktops:output_type -> teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsResponse - 6, // 9: teleport.dynamicwindows.v1.DynamicWindowsService.GetDynamicWindowsDesktop:output_type -> types.DynamicWindowsDesktopV1 - 6, // 10: teleport.dynamicwindows.v1.DynamicWindowsService.CreateDynamicWindowsDesktop:output_type -> types.DynamicWindowsDesktopV1 - 6, // 11: teleport.dynamicwindows.v1.DynamicWindowsService.UpdateDynamicWindowsDesktop:output_type -> types.DynamicWindowsDesktopV1 - 7, // 12: teleport.dynamicwindows.v1.DynamicWindowsService.DeleteDynamicWindowsDesktop:output_type -> google.protobuf.Empty - 8, // [8:13] is the sub-list for method output_type - 3, // [3:8] 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 + 7, // 0: teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsResponse.desktops:type_name -> types.DynamicWindowsDesktopV1 + 7, // 1: teleport.dynamicwindows.v1.CreateDynamicWindowsDesktopRequest.desktop:type_name -> types.DynamicWindowsDesktopV1 + 7, // 2: teleport.dynamicwindows.v1.UpdateDynamicWindowsDesktopRequest.desktop:type_name -> types.DynamicWindowsDesktopV1 + 7, // 3: teleport.dynamicwindows.v1.UpsertDynamicWindowsDesktopRequest.desktop:type_name -> types.DynamicWindowsDesktopV1 + 0, // 4: teleport.dynamicwindows.v1.DynamicWindowsService.ListDynamicWindowsDesktops:input_type -> teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsRequest + 2, // 5: teleport.dynamicwindows.v1.DynamicWindowsService.GetDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.GetDynamicWindowsDesktopRequest + 3, // 6: teleport.dynamicwindows.v1.DynamicWindowsService.CreateDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.CreateDynamicWindowsDesktopRequest + 4, // 7: teleport.dynamicwindows.v1.DynamicWindowsService.UpdateDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.UpdateDynamicWindowsDesktopRequest + 5, // 8: teleport.dynamicwindows.v1.DynamicWindowsService.UpsertDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.UpsertDynamicWindowsDesktopRequest + 6, // 9: teleport.dynamicwindows.v1.DynamicWindowsService.DeleteDynamicWindowsDesktop:input_type -> teleport.dynamicwindows.v1.DeleteDynamicWindowsDesktopRequest + 1, // 10: teleport.dynamicwindows.v1.DynamicWindowsService.ListDynamicWindowsDesktops:output_type -> teleport.dynamicwindows.v1.ListDynamicWindowsDesktopsResponse + 7, // 11: teleport.dynamicwindows.v1.DynamicWindowsService.GetDynamicWindowsDesktop:output_type -> types.DynamicWindowsDesktopV1 + 7, // 12: teleport.dynamicwindows.v1.DynamicWindowsService.CreateDynamicWindowsDesktop:output_type -> types.DynamicWindowsDesktopV1 + 7, // 13: teleport.dynamicwindows.v1.DynamicWindowsService.UpdateDynamicWindowsDesktop:output_type -> types.DynamicWindowsDesktopV1 + 7, // 14: teleport.dynamicwindows.v1.DynamicWindowsService.UpsertDynamicWindowsDesktop:output_type -> types.DynamicWindowsDesktopV1 + 8, // 15: teleport.dynamicwindows.v1.DynamicWindowsService.DeleteDynamicWindowsDesktop:output_type -> google.protobuf.Empty + 10, // [10:16] is the sub-list for method output_type + 4, // [4:10] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_init() } @@ -494,7 +559,7 @@ func file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_dynamicwindows_v1_dynamicwindows_service_proto_rawDesc, NumEnums: 0, - NumMessages: 6, + NumMessages: 7, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service_grpc.pb.go b/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service_grpc.pb.go index 62e0fb6429b..83d36000f70 100644 --- a/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/dynamicwindows/v1/dynamicwindows_service_grpc.pb.go @@ -42,6 +42,7 @@ const ( DynamicWindowsService_GetDynamicWindowsDesktop_FullMethodName = "/teleport.dynamicwindows.v1.DynamicWindowsService/GetDynamicWindowsDesktop" DynamicWindowsService_CreateDynamicWindowsDesktop_FullMethodName = "/teleport.dynamicwindows.v1.DynamicWindowsService/CreateDynamicWindowsDesktop" DynamicWindowsService_UpdateDynamicWindowsDesktop_FullMethodName = "/teleport.dynamicwindows.v1.DynamicWindowsService/UpdateDynamicWindowsDesktop" + DynamicWindowsService_UpsertDynamicWindowsDesktop_FullMethodName = "/teleport.dynamicwindows.v1.DynamicWindowsService/UpsertDynamicWindowsDesktop" DynamicWindowsService_DeleteDynamicWindowsDesktop_FullMethodName = "/teleport.dynamicwindows.v1.DynamicWindowsService/DeleteDynamicWindowsDesktop" ) @@ -59,6 +60,8 @@ type DynamicWindowsServiceClient interface { CreateDynamicWindowsDesktop(ctx context.Context, in *CreateDynamicWindowsDesktopRequest, opts ...grpc.CallOption) (*types.DynamicWindowsDesktopV1, error) // UpdateDynamicWindowsDesktop updates an existing dynamic Windows desktop. UpdateDynamicWindowsDesktop(ctx context.Context, in *UpdateDynamicWindowsDesktopRequest, opts ...grpc.CallOption) (*types.DynamicWindowsDesktopV1, error) + // UpsertDynamicWindowsDesktop updates an existing dynamic Windows desktop or creates new if it doesn't exist. + UpsertDynamicWindowsDesktop(ctx context.Context, in *UpsertDynamicWindowsDesktopRequest, opts ...grpc.CallOption) (*types.DynamicWindowsDesktopV1, error) // DeleteDynamicWindowsDesktop removes the specified dynamic Windows desktop. DeleteDynamicWindowsDesktop(ctx context.Context, in *DeleteDynamicWindowsDesktopRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) } @@ -111,6 +114,16 @@ func (c *dynamicWindowsServiceClient) UpdateDynamicWindowsDesktop(ctx context.Co return out, nil } +func (c *dynamicWindowsServiceClient) UpsertDynamicWindowsDesktop(ctx context.Context, in *UpsertDynamicWindowsDesktopRequest, opts ...grpc.CallOption) (*types.DynamicWindowsDesktopV1, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(types.DynamicWindowsDesktopV1) + err := c.cc.Invoke(ctx, DynamicWindowsService_UpsertDynamicWindowsDesktop_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *dynamicWindowsServiceClient) DeleteDynamicWindowsDesktop(ctx context.Context, in *DeleteDynamicWindowsDesktopRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(emptypb.Empty) @@ -135,6 +148,8 @@ type DynamicWindowsServiceServer interface { CreateDynamicWindowsDesktop(context.Context, *CreateDynamicWindowsDesktopRequest) (*types.DynamicWindowsDesktopV1, error) // UpdateDynamicWindowsDesktop updates an existing dynamic Windows desktop. UpdateDynamicWindowsDesktop(context.Context, *UpdateDynamicWindowsDesktopRequest) (*types.DynamicWindowsDesktopV1, error) + // UpsertDynamicWindowsDesktop updates an existing dynamic Windows desktop or creates new if it doesn't exist. + UpsertDynamicWindowsDesktop(context.Context, *UpsertDynamicWindowsDesktopRequest) (*types.DynamicWindowsDesktopV1, error) // DeleteDynamicWindowsDesktop removes the specified dynamic Windows desktop. DeleteDynamicWindowsDesktop(context.Context, *DeleteDynamicWindowsDesktopRequest) (*emptypb.Empty, error) mustEmbedUnimplementedDynamicWindowsServiceServer() @@ -159,6 +174,9 @@ func (UnimplementedDynamicWindowsServiceServer) CreateDynamicWindowsDesktop(cont func (UnimplementedDynamicWindowsServiceServer) UpdateDynamicWindowsDesktop(context.Context, *UpdateDynamicWindowsDesktopRequest) (*types.DynamicWindowsDesktopV1, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateDynamicWindowsDesktop not implemented") } +func (UnimplementedDynamicWindowsServiceServer) UpsertDynamicWindowsDesktop(context.Context, *UpsertDynamicWindowsDesktopRequest) (*types.DynamicWindowsDesktopV1, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpsertDynamicWindowsDesktop not implemented") +} func (UnimplementedDynamicWindowsServiceServer) DeleteDynamicWindowsDesktop(context.Context, *DeleteDynamicWindowsDesktopRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteDynamicWindowsDesktop not implemented") } @@ -255,6 +273,24 @@ func _DynamicWindowsService_UpdateDynamicWindowsDesktop_Handler(srv interface{}, return interceptor(ctx, in, info, handler) } +func _DynamicWindowsService_UpsertDynamicWindowsDesktop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertDynamicWindowsDesktopRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DynamicWindowsServiceServer).UpsertDynamicWindowsDesktop(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DynamicWindowsService_UpsertDynamicWindowsDesktop_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DynamicWindowsServiceServer).UpsertDynamicWindowsDesktop(ctx, req.(*UpsertDynamicWindowsDesktopRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _DynamicWindowsService_DeleteDynamicWindowsDesktop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteDynamicWindowsDesktopRequest) if err := dec(in); err != nil { @@ -296,6 +332,10 @@ var DynamicWindowsService_ServiceDesc = grpc.ServiceDesc{ MethodName: "UpdateDynamicWindowsDesktop", Handler: _DynamicWindowsService_UpdateDynamicWindowsDesktop_Handler, }, + { + MethodName: "UpsertDynamicWindowsDesktop", + Handler: _DynamicWindowsService_UpsertDynamicWindowsDesktop_Handler, + }, { MethodName: "DeleteDynamicWindowsDesktop", Handler: _DynamicWindowsService_DeleteDynamicWindowsDesktop_Handler, diff --git a/api/proto/teleport/dynamicwindows/v1/dynamicwindows_service.proto b/api/proto/teleport/dynamicwindows/v1/dynamicwindows_service.proto index 63c899c6cbd..718cfb51456 100644 --- a/api/proto/teleport/dynamicwindows/v1/dynamicwindows_service.proto +++ b/api/proto/teleport/dynamicwindows/v1/dynamicwindows_service.proto @@ -35,6 +35,8 @@ service DynamicWindowsService { rpc CreateDynamicWindowsDesktop(CreateDynamicWindowsDesktopRequest) returns (types.DynamicWindowsDesktopV1); // UpdateDynamicWindowsDesktop updates an existing dynamic Windows desktop. rpc UpdateDynamicWindowsDesktop(UpdateDynamicWindowsDesktopRequest) returns (types.DynamicWindowsDesktopV1); + // UpsertDynamicWindowsDesktop updates an existing dynamic Windows desktop or creates new if it doesn't exist. + rpc UpsertDynamicWindowsDesktop(UpsertDynamicWindowsDesktopRequest) returns (types.DynamicWindowsDesktopV1); // DeleteDynamicWindowsDesktop removes the specified dynamic Windows desktop. rpc DeleteDynamicWindowsDesktop(DeleteDynamicWindowsDesktopRequest) returns (google.protobuf.Empty); } @@ -63,18 +65,24 @@ message GetDynamicWindowsDesktopRequest { string name = 1; } -// CreateDynamicWindowsDesktopRequest is a request for a specific dynamic Windows desktop. +// CreateDynamicWindowsDesktopRequest is used for creating new dynamic Windows desktops. message CreateDynamicWindowsDesktopRequest { // desktop to be created types.DynamicWindowsDesktopV1 desktop = 1; } -// UpdateDynamicWindowsDesktopRequest is a request for a specific dynamic Windows desktop. +// UpdateDynamicWindowsDesktopRequest is used for updating existing dynamic Windows desktops. message UpdateDynamicWindowsDesktopRequest { // desktop to be updated types.DynamicWindowsDesktopV1 desktop = 1; } +// UpsertDynamicWindowsDesktopRequest is used for upserting dynamic Windows desktops. +message UpsertDynamicWindowsDesktopRequest { + // desktop to be upserted + types.DynamicWindowsDesktopV1 desktop = 1; +} + // DeleteDynamicWindowsDesktopRequest is a request to delete a Windows desktop host. message DeleteDynamicWindowsDesktopRequest { // name is the name of the Windows desktop host. diff --git a/lib/auth/accesspoint/accesspoint.go b/lib/auth/accesspoint/accesspoint.go index 5b0d4b6084f..d9ac852bba6 100644 --- a/lib/auth/accesspoint/accesspoint.go +++ b/lib/auth/accesspoint/accesspoint.go @@ -103,6 +103,7 @@ type Config struct { Users services.UsersService WebSession types.WebSessionInterface WebToken types.WebTokenInterface + DynamicWindowsDesktops services.DynamicWindowsDesktops WindowsDesktops services.WindowsDesktops AutoUpdateService services.AutoUpdateServiceGetter ProvisioningStates services.ProvisioningStates @@ -201,6 +202,7 @@ func NewCache(cfg Config) (*cache.Cache, error) { WebSession: cfg.WebSession, WebToken: cfg.WebToken, WindowsDesktops: cfg.WindowsDesktops, + DynamicWindowsDesktops: cfg.DynamicWindowsDesktops, ProvisioningStates: cfg.ProvisioningStates, IdentityCenter: cfg.IdentityCenter, } diff --git a/lib/auth/dynamicwindows/dynamicwindowsv1/service.go b/lib/auth/dynamicwindows/dynamicwindowsv1/service.go index 98bc2f81e6b..5a42eefe8ed 100644 --- a/lib/auth/dynamicwindows/dynamicwindowsv1/service.go +++ b/lib/auth/dynamicwindows/dynamicwindowsv1/service.go @@ -196,6 +196,31 @@ func (s *Service) UpdateDynamicWindowsDesktop(ctx context.Context, req *dynamicw return updatedDesktop, nil } +// UpsertDynamicWindowsDesktop updates an existing dynamic Windows desktop or creates one if it doesn't exist. +func (s *Service) UpsertDynamicWindowsDesktop(ctx context.Context, req *dynamicwindowspb.UpsertDynamicWindowsDesktopRequest) (*types.DynamicWindowsDesktopV1, error) { + auth, err := s.authorizer.Authorize(ctx) + if err != nil { + return nil, trace.Wrap(err) + } + if err := auth.AuthorizeAdminAction(); err != nil { + return nil, trace.Wrap(err) + } + if err := auth.CheckAccessToKind(types.KindDynamicWindowsDesktop, types.VerbCreate, types.VerbUpdate); err != nil { + return nil, trace.Wrap(err) + } + d, err := s.backend.UpsertDynamicWindowsDesktop(ctx, req.Desktop) + if err != nil { + return nil, trace.Wrap(err) + } + + updatedDesktop, ok := d.(*types.DynamicWindowsDesktopV1) + if !ok { + return nil, trace.BadParameter("unexpected type %T", d) + } + + return updatedDesktop, nil +} + // DeleteDynamicWindowsDesktop removes the specified dynamic Windows desktop. func (s *Service) DeleteDynamicWindowsDesktop(ctx context.Context, req *dynamicwindowspb.DeleteDynamicWindowsDesktopRequest) (*emptypb.Empty, error) { auth, err := s.authorizer.Authorize(ctx) diff --git a/lib/auth/dynamicwindows/dynamicwindowsv1/service_test.go b/lib/auth/dynamicwindows/dynamicwindowsv1/service_test.go index 8fee09af10d..1c474f7192b 100644 --- a/lib/auth/dynamicwindows/dynamicwindowsv1/service_test.go +++ b/lib/auth/dynamicwindows/dynamicwindowsv1/service_test.go @@ -55,6 +55,11 @@ func TestServiceAccess(t *testing.T) { allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, allowedVerbs: []string{types.VerbUpdate}, }, + { + name: "UpsertDynamicWindowsDesktop", + allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, + allowedVerbs: []string{types.VerbCreate, types.VerbUpdate}, + }, { name: "DeleteDynamicWindowsDesktop", allowedStates: []authz.AdminActionAuthState{authz.AdminActionAuthNotRequired, authz.AdminActionAuthMFAVerified}, @@ -160,6 +165,10 @@ func callMethod(service *Service, method string) error { arg.Desktop, _ = types.NewDynamicWindowsDesktopV1("test", nil, types.DynamicWindowsDesktopSpecV1{ Addr: "test", }) + case *dynamicwindowsv1.UpsertDynamicWindowsDesktopRequest: + arg.Desktop, _ = types.NewDynamicWindowsDesktopV1("test", nil, types.DynamicWindowsDesktopSpecV1{ + Addr: "test", + }) } return nil }, nil) diff --git a/lib/auth/helpers.go b/lib/auth/helpers.go index 851cca043ad..9553f2697cf 100644 --- a/lib/auth/helpers.go +++ b/lib/auth/helpers.go @@ -366,6 +366,7 @@ func NewTestAuthServer(cfg TestAuthServerConfig) (*TestAuthServer, error) { WebSession: svces.Identity.WebSessions(), WebToken: svces.WebTokens(), WindowsDesktops: svces.WindowsDesktops, + DynamicWindowsDesktops: svces.DynamicWindowsDesktops, }) if err != nil { return nil, trace.Wrap(err) diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index f74ca9eeef1..7f3e6144cf7 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -121,6 +121,7 @@ type testPack struct { webSessionS types.WebSessionInterface webTokenS types.WebTokenInterface windowsDesktops services.WindowsDesktops + dynamicWindowsDesktops services.DynamicWindowsDesktops samlIDPServiceProviders services.SAMLIdPServiceProviders userGroups services.UserGroups okta services.Okta @@ -269,6 +270,11 @@ func newPackWithoutCache(dir string, opts ...packOption) (*testPack, error) { return nil, trace.Wrap(err) } + dynamicWindowsDesktopService, err := local.NewDynamicWindowsDesktopService(p.backend) + if err != nil { + return nil, trace.Wrap(err) + } + p.trustS = local.NewCAService(p.backend) p.clusterConfigS = clusterConfig p.provisionerS = local.NewProvisioningService(p.backend) @@ -288,6 +294,7 @@ func newPackWithoutCache(dir string, opts ...packOption) (*testPack, error) { p.databases = local.NewDatabasesService(p.backend) p.databaseServices = local.NewDatabaseServicesService(p.backend) p.windowsDesktops = local.NewWindowsDesktopService(p.backend) + p.dynamicWindowsDesktops = dynamicWindowsDesktopService p.samlIDPServiceProviders, err = local.NewSAMLIdPServiceProviderService(p.backend) if err != nil { return nil, trace.Wrap(err) @@ -428,6 +435,7 @@ func newPack(dir string, setupConfig func(c Config) Config, opts ...packOption) DatabaseServices: p.databaseServices, Databases: p.databases, WindowsDesktops: p.windowsDesktops, + DynamicWindowsDesktops: p.dynamicWindowsDesktops, SAMLIdPServiceProviders: p.samlIDPServiceProviders, UserGroups: p.userGroups, Okta: p.okta, @@ -657,6 +665,7 @@ func TestNodeCAFiltering(t *testing.T) { WebSession: p.cache.webSessionCache, WebToken: p.cache.webTokenCache, WindowsDesktops: p.cache.windowsDesktopsCache, + DynamicWindowsDesktops: p.cache.dynamicWindowsDesktopsCache, SAMLIdPServiceProviders: p.samlIDPServiceProviders, UserGroups: p.userGroups, StaticHostUsers: p.staticHostUsers, @@ -838,6 +847,7 @@ func TestCompletenessInit(t *testing.T) { DatabaseServices: p.databaseServices, Databases: p.databases, WindowsDesktops: p.windowsDesktops, + DynamicWindowsDesktops: p.dynamicWindowsDesktops, SAMLIdPServiceProviders: p.samlIDPServiceProviders, UserGroups: p.userGroups, Okta: p.okta, @@ -921,6 +931,7 @@ func TestCompletenessReset(t *testing.T) { DatabaseServices: p.databaseServices, Databases: p.databases, WindowsDesktops: p.windowsDesktops, + DynamicWindowsDesktops: p.dynamicWindowsDesktops, SAMLIdPServiceProviders: p.samlIDPServiceProviders, UserGroups: p.userGroups, Okta: p.okta, @@ -1130,6 +1141,7 @@ func TestListResources_NodesTTLVariant(t *testing.T) { DatabaseServices: p.databaseServices, Databases: p.databases, WindowsDesktops: p.windowsDesktops, + DynamicWindowsDesktops: p.dynamicWindowsDesktops, SAMLIdPServiceProviders: p.samlIDPServiceProviders, UserGroups: p.userGroups, Okta: p.okta, @@ -1224,6 +1236,7 @@ func initStrategy(t *testing.T) { DatabaseServices: p.databaseServices, Databases: p.databases, WindowsDesktops: p.windowsDesktops, + DynamicWindowsDesktops: p.dynamicWindowsDesktops, SAMLIdPServiceProviders: p.samlIDPServiceProviders, UserGroups: p.userGroups, Okta: p.okta, diff --git a/lib/cache/collections.go b/lib/cache/collections.go index 17c28934a32..f7501f1bfda 100644 --- a/lib/cache/collections.go +++ b/lib/cache/collections.go @@ -2328,7 +2328,7 @@ func (dynamicWindowsDesktopsExecutor) getAll(ctx context.Context, cache *Cache, var desktops []types.DynamicWindowsDesktop next := "" for { - d, token, err := cache.dynamicWindowsDesktopsCache.ListDynamicWindowsDesktops(ctx, defaults.MaxIterationLimit, next) + d, token, err := cache.Config.DynamicWindowsDesktops.ListDynamicWindowsDesktops(ctx, defaults.MaxIterationLimit, next) if err != nil { return nil, err } diff --git a/lib/service/service.go b/lib/service/service.go index 215fdb0035f..cef12700598 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -2529,6 +2529,7 @@ func (process *TeleportProcess) newAccessCacheForServices(cfg accesspoint.Config cfg.WebSession = services.Identity.WebSessions() cfg.WebToken = services.Identity.WebTokens() cfg.WindowsDesktops = services.WindowsDesktops + cfg.DynamicWindowsDesktops = services.DynamicWindowsDesktops cfg.AutoUpdateService = services.AutoUpdateService cfg.ProvisioningStates = services.ProvisioningStates cfg.IdentityCenter = services.IdentityCenter @@ -2576,6 +2577,7 @@ func (process *TeleportProcess) newAccessCacheForClient(cfg accesspoint.Config, cfg.WebSession = client.WebSessions() cfg.WebToken = client.WebTokens() cfg.WindowsDesktops = client + cfg.DynamicWindowsDesktops = client.DynamicDesktopClient() cfg.AutoUpdateService = client return accesspoint.NewCache(cfg) diff --git a/lib/services/local/dynamic_desktops.go b/lib/services/local/dynamic_desktops.go index b4b482d600d..6254db2bd2a 100644 --- a/lib/services/local/dynamic_desktops.go +++ b/lib/services/local/dynamic_desktops.go @@ -73,7 +73,11 @@ func (s *DynamicWindowsDesktopService) CreateDynamicWindowsDesktop(ctx context.C // UpdateDynamicWindowsDesktop updates a dynamic Windows desktop resource. func (s *DynamicWindowsDesktopService) UpdateDynamicWindowsDesktop(ctx context.Context, desktop types.DynamicWindowsDesktop) (types.DynamicWindowsDesktop, error) { - d, err := s.service.UpdateResource(ctx, desktop) + // ConditionalUpdateResource can return invalid revision instead of not found, so we'll check if resource exists first + if _, err := s.service.GetResource(ctx, desktop.GetName()); trace.IsNotFound(err) { + return nil, err + } + d, err := s.service.ConditionalUpdateResource(ctx, desktop) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/services/local/dynamic_desktops_test.go b/lib/services/local/dynamic_desktops_test.go index 75ed0400806..9e20ed30e7e 100644 --- a/lib/services/local/dynamic_desktops_test.go +++ b/lib/services/local/dynamic_desktops_test.go @@ -182,8 +182,15 @@ func TestDynamicWindowsService_UpdateDynamicDesktop(t *testing.T) { require.Error(t, err) require.True(t, trace.IsNotFound(err)) }) + t.Run("revision doesn't match", func(t *testing.T) { + want := newDynamicDesktop(t, "example1") + _, err := service.CreateDynamicWindowsDesktop(ctx, want.Copy()) + require.NoError(t, err) + _, err = service.UpdateDynamicWindowsDesktop(ctx, want) + require.Error(t, err) + }) t.Run("ok", func(t *testing.T) { - want := newDynamicDesktop(t, "example") + want := newDynamicDesktop(t, "example2") created, err := service.CreateDynamicWindowsDesktop(ctx, want.Copy()) require.NoError(t, err) updated, err := service.UpdateDynamicWindowsDesktop(ctx, created.Copy()) diff --git a/lib/srv/desktop/discovery_test.go b/lib/srv/desktop/discovery_test.go index fc188f75ce1..01941e02d00 100644 --- a/lib/srv/desktop/discovery_test.go +++ b/lib/srv/desktop/discovery_test.go @@ -279,7 +279,7 @@ func TestDynamicWindowsDiscovery(t *testing.T) { } desktop.Spec.Addr = "addr2" - _, err = dynamicWindowsClient.UpdateDynamicWindowsDesktop(ctx, desktop) + _, err = dynamicWindowsClient.UpsertDynamicWindowsDesktop(ctx, desktop) require.NoError(t, err) time.Sleep(10 * time.Millisecond) diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index c37e8805581..32a07121b63 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -904,7 +904,7 @@ func (rc *ResourceCommand) createDynamicWindowsDesktop(ctx context.Context, clie if !rc.force { return trace.AlreadyExists("application %q already exists", wd.GetName()) } - if _, err := dynamicDesktopClient.UpdateDynamicWindowsDesktop(ctx, wd); err != nil { + if _, err := dynamicDesktopClient.UpsertDynamicWindowsDesktop(ctx, wd); err != nil { return trace.Wrap(err) } fmt.Printf("dynamic windows desktop %q has been updated\n", wd.GetName())