diff --git a/api/client/dynamicwindows/dynamicwindows.go b/api/client/dynamicwindows/dynamicwindows.go index 32ba1762f1aed..19d89e619fac8 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 b09748485f162..3523ac1e79d57 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 62e0fb6429b26..83d36000f707f 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 63c899c6cbd2d..718cfb5145611 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 5b0d4b6084f07..d9ac852bba65b 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 98bc2f81e6b55..5a42eefe8edca 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 8fee09af10dfb..1c474f7192be9 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 851cca043ad92..9553f2697cf8c 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 f74ca9eeef151..7f3e6144cf79a 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 17c28934a32c3..f7501f1bfdad8 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 215fdb0035f00..cef1270059802 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 b4b482d600de7..6254db2bd2a34 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 75ed040080648..9e20ed30e7eb2 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 fc188f75ce1d6..01941e02d0056 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 c37e8805581e6..32a07121b63ca 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())