diff --git a/api/client/discoveryconfig/discoveryconfig.go b/api/client/discoveryconfig/discoveryconfig.go index ca531609e5096..60bacb2d42294 100644 --- a/api/client/discoveryconfig/discoveryconfig.go +++ b/api/client/discoveryconfig/discoveryconfig.go @@ -96,6 +96,18 @@ func (c *Client) UpdateDiscoveryConfig(ctx context.Context, discoveryConfig *dis return dc, trace.Wrap(err) } +// UpsertDiscoveryConfig creates or updates a DiscoveryConfig. +func (c *Client) UpsertDiscoveryConfig(ctx context.Context, discoveryConfig *discoveryconfig.DiscoveryConfig) (*discoveryconfig.DiscoveryConfig, error) { + resp, err := c.grpcClient.UpsertDiscoveryConfig(ctx, &discoveryconfigv1.UpsertDiscoveryConfigRequest{ + DiscoveryConfig: conv.ToProto(discoveryConfig), + }) + if err != nil { + return nil, trace.Wrap(err) + } + dc, err := conv.FromProto(resp) + return dc, trace.Wrap(err) +} + // DeleteDiscoveryConfig removes the specified DiscoveryConfig resource. func (c *Client) DeleteDiscoveryConfig(ctx context.Context, name string) error { _, err := c.grpcClient.DeleteDiscoveryConfig(ctx, &discoveryconfigv1.DeleteDiscoveryConfigRequest{ diff --git a/api/client/events.go b/api/client/events.go index d8d660ea37c29..a881ca9d8d74f 100644 --- a/api/client/events.go +++ b/api/client/events.go @@ -21,6 +21,8 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" accesslistv1conv "github.com/gravitational/teleport/api/types/accesslist/convert/v1" + "github.com/gravitational/teleport/api/types/discoveryconfig" + discoveryconfigv1conv "github.com/gravitational/teleport/api/types/discoveryconfig/convert/v1" "github.com/gravitational/teleport/api/types/userloginstate" userloginstatev1conv "github.com/gravitational/teleport/api/types/userloginstate/convert/v1" ) @@ -226,6 +228,10 @@ func EventToGRPC(in types.Event) (*proto.Event, error) { out.Resource = &proto.Event_AccessListMember{ AccessListMember: accesslistv1conv.ToMemberProto(r), } + case *discoveryconfig.DiscoveryConfig: + out.Resource = &proto.Event_DiscoveryConfig{ + DiscoveryConfig: discoveryconfigv1conv.ToProto(r), + } default: return nil, trace.BadParameter("resource type %T is not supported", in.Resource) } @@ -399,6 +405,12 @@ func EventFromGRPC(in *proto.Event) (*types.Event, error) { return nil, trace.Wrap(err) } return &out, nil + } else if r := in.GetDiscoveryConfig(); r != nil { + out.Resource, err = discoveryconfigv1conv.FromProto(r) + if err != nil { + return nil, trace.Wrap(err) + } + return &out, nil } else { return nil, trace.BadParameter("received unsupported resource %T", in.Resource) } diff --git a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go index 8419d38c3c510..47d87275d24d0 100644 --- a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go +++ b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service.pb.go @@ -307,6 +307,55 @@ func (x *UpdateDiscoveryConfigRequest) GetDiscoveryConfig() *DiscoveryConfig { return nil } +// UpsertDiscoveryConfigRequest is the request to upsert the provided DiscoveryConfig. +type UpsertDiscoveryConfigRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // DiscoveryConfig is the DiscoveryConfig to be upserted. + DiscoveryConfig *DiscoveryConfig `protobuf:"bytes,1,opt,name=discovery_config,json=discoveryConfig,proto3" json:"discovery_config,omitempty"` +} + +func (x *UpsertDiscoveryConfigRequest) Reset() { + *x = UpsertDiscoveryConfigRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *UpsertDiscoveryConfigRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpsertDiscoveryConfigRequest) ProtoMessage() {} + +func (x *UpsertDiscoveryConfigRequest) ProtoReflect() protoreflect.Message { + mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use UpsertDiscoveryConfigRequest.ProtoReflect.Descriptor instead. +func (*UpsertDiscoveryConfigRequest) Descriptor() ([]byte, []int) { + return file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDescGZIP(), []int{5} +} + +func (x *UpsertDiscoveryConfigRequest) GetDiscoveryConfig() *DiscoveryConfig { + if x != nil { + return x.DiscoveryConfig + } + return nil +} + // DeleteDiscoveryConfigRequest is a request for deleting a specific DiscoveryConfig resource. type DeleteDiscoveryConfigRequest struct { state protoimpl.MessageState @@ -320,7 +369,7 @@ type DeleteDiscoveryConfigRequest struct { func (x *DeleteDiscoveryConfigRequest) Reset() { *x = DeleteDiscoveryConfigRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[5] + mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -333,7 +382,7 @@ func (x *DeleteDiscoveryConfigRequest) String() string { func (*DeleteDiscoveryConfigRequest) ProtoMessage() {} func (x *DeleteDiscoveryConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[5] + mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -346,7 +395,7 @@ func (x *DeleteDiscoveryConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteDiscoveryConfigRequest.ProtoReflect.Descriptor instead. func (*DeleteDiscoveryConfigRequest) Descriptor() ([]byte, []int) { - return file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDescGZIP(), []int{5} + return file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDescGZIP(), []int{6} } func (x *DeleteDiscoveryConfigRequest) GetName() string { @@ -366,7 +415,7 @@ type DeleteAllDiscoveryConfigsRequest struct { func (x *DeleteAllDiscoveryConfigsRequest) Reset() { *x = DeleteAllDiscoveryConfigsRequest{} if protoimpl.UnsafeEnabled { - mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[6] + mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -379,7 +428,7 @@ func (x *DeleteAllDiscoveryConfigsRequest) String() string { func (*DeleteAllDiscoveryConfigsRequest) ProtoMessage() {} func (x *DeleteAllDiscoveryConfigsRequest) ProtoReflect() protoreflect.Message { - mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[6] + mi := &file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -392,7 +441,7 @@ func (x *DeleteAllDiscoveryConfigsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteAllDiscoveryConfigsRequest.ProtoReflect.Descriptor instead. func (*DeleteAllDiscoveryConfigsRequest) Descriptor() ([]byte, []int) { - return file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDescGZIP(), []int{6} + return file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDescGZIP(), []int{7} } var File_teleport_discoveryconfig_v1_discoveryconfig_service_proto protoreflect.FileDescriptor @@ -443,68 +492,84 @@ var file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDesc = []b 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, 0x64, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x32, - 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x22, 0x22, 0x0a, 0x20, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x44, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, 0x88, 0x06, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x12, 0x8b, 0x01, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x38, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x73, - 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, - 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x7a, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, - 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x80, 0x01, 0x0a, 0x15, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, - 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, - 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x80, - 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, - 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, + 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x77, + 0x0a, 0x1c, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x57, + 0x0a, 0x10, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, - 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, + 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x32, 0x0a, 0x1c, 0x44, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 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, 0x22, 0x22, 0x0a, 0x20, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x32, + 0x8b, 0x07, 0x0a, 0x16, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x8b, 0x01, 0x0a, 0x14, 0x4c, + 0x69, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x73, 0x12, 0x38, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, - 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x12, 0x6a, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, - 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, - 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, + 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x39, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7a, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x44, + 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x36, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, + 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x44, + 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, + 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, + 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x74, + 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x80, 0x01, 0x0a, 0x15, 0x55, + 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x76, 0x31, 0x2e, 0x55, 0x70, 0x73, 0x65, 0x72, 0x74, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, + 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x2c, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x6a, 0x0a, + 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x39, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x44, 0x69, 0x73, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 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, 0x12, 0x72, 0x0a, 0x19, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x44, 0x69, + 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 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, 0x12, 0x72, 0x0a, - 0x19, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, - 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x3d, 0x2e, 0x74, 0x65, 0x6c, - 0x65, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x41, - 0x6c, 0x6c, 0x44, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x73, 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, 0x62, 0x5a, 0x60, 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, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2f, 0x76, 0x31, 0x3b, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x62, 0x5a, + 0x60, 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, 0x69, 0x73, + 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x76, 0x31, 0x3b, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -519,39 +584,43 @@ func file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDescGZIP( return file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDescData } -var file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_goTypes = []interface{}{ (*ListDiscoveryConfigsRequest)(nil), // 0: teleport.discoveryconfig.v1.ListDiscoveryConfigsRequest (*ListDiscoveryConfigsResponse)(nil), // 1: teleport.discoveryconfig.v1.ListDiscoveryConfigsResponse (*GetDiscoveryConfigRequest)(nil), // 2: teleport.discoveryconfig.v1.GetDiscoveryConfigRequest (*CreateDiscoveryConfigRequest)(nil), // 3: teleport.discoveryconfig.v1.CreateDiscoveryConfigRequest (*UpdateDiscoveryConfigRequest)(nil), // 4: teleport.discoveryconfig.v1.UpdateDiscoveryConfigRequest - (*DeleteDiscoveryConfigRequest)(nil), // 5: teleport.discoveryconfig.v1.DeleteDiscoveryConfigRequest - (*DeleteAllDiscoveryConfigsRequest)(nil), // 6: teleport.discoveryconfig.v1.DeleteAllDiscoveryConfigsRequest - (*DiscoveryConfig)(nil), // 7: teleport.discoveryconfig.v1.DiscoveryConfig - (*emptypb.Empty)(nil), // 8: google.protobuf.Empty + (*UpsertDiscoveryConfigRequest)(nil), // 5: teleport.discoveryconfig.v1.UpsertDiscoveryConfigRequest + (*DeleteDiscoveryConfigRequest)(nil), // 6: teleport.discoveryconfig.v1.DeleteDiscoveryConfigRequest + (*DeleteAllDiscoveryConfigsRequest)(nil), // 7: teleport.discoveryconfig.v1.DeleteAllDiscoveryConfigsRequest + (*DiscoveryConfig)(nil), // 8: teleport.discoveryconfig.v1.DiscoveryConfig + (*emptypb.Empty)(nil), // 9: google.protobuf.Empty } var file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_depIdxs = []int32{ - 7, // 0: teleport.discoveryconfig.v1.ListDiscoveryConfigsResponse.discovery_configs:type_name -> teleport.discoveryconfig.v1.DiscoveryConfig - 7, // 1: teleport.discoveryconfig.v1.CreateDiscoveryConfigRequest.discovery_config:type_name -> teleport.discoveryconfig.v1.DiscoveryConfig - 7, // 2: teleport.discoveryconfig.v1.UpdateDiscoveryConfigRequest.discovery_config:type_name -> teleport.discoveryconfig.v1.DiscoveryConfig - 0, // 3: teleport.discoveryconfig.v1.DiscoveryConfigService.ListDiscoveryConfigs:input_type -> teleport.discoveryconfig.v1.ListDiscoveryConfigsRequest - 2, // 4: teleport.discoveryconfig.v1.DiscoveryConfigService.GetDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.GetDiscoveryConfigRequest - 3, // 5: teleport.discoveryconfig.v1.DiscoveryConfigService.CreateDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.CreateDiscoveryConfigRequest - 4, // 6: teleport.discoveryconfig.v1.DiscoveryConfigService.UpdateDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.UpdateDiscoveryConfigRequest - 5, // 7: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.DeleteDiscoveryConfigRequest - 6, // 8: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteAllDiscoveryConfigs:input_type -> teleport.discoveryconfig.v1.DeleteAllDiscoveryConfigsRequest - 1, // 9: teleport.discoveryconfig.v1.DiscoveryConfigService.ListDiscoveryConfigs:output_type -> teleport.discoveryconfig.v1.ListDiscoveryConfigsResponse - 7, // 10: teleport.discoveryconfig.v1.DiscoveryConfigService.GetDiscoveryConfig:output_type -> teleport.discoveryconfig.v1.DiscoveryConfig - 7, // 11: teleport.discoveryconfig.v1.DiscoveryConfigService.CreateDiscoveryConfig:output_type -> teleport.discoveryconfig.v1.DiscoveryConfig - 7, // 12: teleport.discoveryconfig.v1.DiscoveryConfigService.UpdateDiscoveryConfig:output_type -> teleport.discoveryconfig.v1.DiscoveryConfig - 8, // 13: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteDiscoveryConfig:output_type -> google.protobuf.Empty - 8, // 14: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteAllDiscoveryConfigs:output_type -> google.protobuf.Empty - 9, // [9:15] is the sub-list for method output_type - 3, // [3:9] 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 + 8, // 0: teleport.discoveryconfig.v1.ListDiscoveryConfigsResponse.discovery_configs:type_name -> teleport.discoveryconfig.v1.DiscoveryConfig + 8, // 1: teleport.discoveryconfig.v1.CreateDiscoveryConfigRequest.discovery_config:type_name -> teleport.discoveryconfig.v1.DiscoveryConfig + 8, // 2: teleport.discoveryconfig.v1.UpdateDiscoveryConfigRequest.discovery_config:type_name -> teleport.discoveryconfig.v1.DiscoveryConfig + 8, // 3: teleport.discoveryconfig.v1.UpsertDiscoveryConfigRequest.discovery_config:type_name -> teleport.discoveryconfig.v1.DiscoveryConfig + 0, // 4: teleport.discoveryconfig.v1.DiscoveryConfigService.ListDiscoveryConfigs:input_type -> teleport.discoveryconfig.v1.ListDiscoveryConfigsRequest + 2, // 5: teleport.discoveryconfig.v1.DiscoveryConfigService.GetDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.GetDiscoveryConfigRequest + 3, // 6: teleport.discoveryconfig.v1.DiscoveryConfigService.CreateDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.CreateDiscoveryConfigRequest + 4, // 7: teleport.discoveryconfig.v1.DiscoveryConfigService.UpdateDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.UpdateDiscoveryConfigRequest + 5, // 8: teleport.discoveryconfig.v1.DiscoveryConfigService.UpsertDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.UpsertDiscoveryConfigRequest + 6, // 9: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteDiscoveryConfig:input_type -> teleport.discoveryconfig.v1.DeleteDiscoveryConfigRequest + 7, // 10: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteAllDiscoveryConfigs:input_type -> teleport.discoveryconfig.v1.DeleteAllDiscoveryConfigsRequest + 1, // 11: teleport.discoveryconfig.v1.DiscoveryConfigService.ListDiscoveryConfigs:output_type -> teleport.discoveryconfig.v1.ListDiscoveryConfigsResponse + 8, // 12: teleport.discoveryconfig.v1.DiscoveryConfigService.GetDiscoveryConfig:output_type -> teleport.discoveryconfig.v1.DiscoveryConfig + 8, // 13: teleport.discoveryconfig.v1.DiscoveryConfigService.CreateDiscoveryConfig:output_type -> teleport.discoveryconfig.v1.DiscoveryConfig + 8, // 14: teleport.discoveryconfig.v1.DiscoveryConfigService.UpdateDiscoveryConfig:output_type -> teleport.discoveryconfig.v1.DiscoveryConfig + 8, // 15: teleport.discoveryconfig.v1.DiscoveryConfigService.UpsertDiscoveryConfig:output_type -> teleport.discoveryconfig.v1.DiscoveryConfig + 9, // 16: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteDiscoveryConfig:output_type -> google.protobuf.Empty + 9, // 17: teleport.discoveryconfig.v1.DiscoveryConfigService.DeleteAllDiscoveryConfigs:output_type -> google.protobuf.Empty + 11, // [11:18] is the sub-list for method output_type + 4, // [4:11] 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_discoveryconfig_v1_discoveryconfig_service_proto_init() } @@ -622,7 +691,7 @@ func file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_init() { } } file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*DeleteDiscoveryConfigRequest); i { + switch v := v.(*UpsertDiscoveryConfigRequest); i { case 0: return &v.state case 1: @@ -634,6 +703,18 @@ func file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_init() { } } file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*DeleteDiscoveryConfigRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*DeleteAllDiscoveryConfigsRequest); i { case 0: return &v.state @@ -652,7 +733,7 @@ func file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_teleport_discoveryconfig_v1_discoveryconfig_service_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 1, }, diff --git a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go index 8f7bb7f5e9d01..b5aebac7b8294 100644 --- a/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go +++ b/api/gen/proto/go/teleport/discoveryconfig/v1/discoveryconfig_service_grpc.pb.go @@ -38,6 +38,7 @@ const ( DiscoveryConfigService_GetDiscoveryConfig_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/GetDiscoveryConfig" DiscoveryConfigService_CreateDiscoveryConfig_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/CreateDiscoveryConfig" DiscoveryConfigService_UpdateDiscoveryConfig_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/UpdateDiscoveryConfig" + DiscoveryConfigService_UpsertDiscoveryConfig_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/UpsertDiscoveryConfig" DiscoveryConfigService_DeleteDiscoveryConfig_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/DeleteDiscoveryConfig" DiscoveryConfigService_DeleteAllDiscoveryConfigs_FullMethodName = "/teleport.discoveryconfig.v1.DiscoveryConfigService/DeleteAllDiscoveryConfigs" ) @@ -54,6 +55,8 @@ type DiscoveryConfigServiceClient interface { CreateDiscoveryConfig(ctx context.Context, in *CreateDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) // UpdateDiscoveryConfig updates an existing DiscoveryConfig resource. UpdateDiscoveryConfig(ctx context.Context, in *UpdateDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) + // UpsertDiscoveryConfig creates or updates a DiscoveryConfig resource. + UpsertDiscoveryConfig(ctx context.Context, in *UpsertDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) // DeleteDiscoveryConfig removes the specified DiscoveryConfig resource. DeleteDiscoveryConfig(ctx context.Context, in *DeleteDiscoveryConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) // DeleteAllDiscoveryConfigs removes all DiscoveryConfigs. @@ -104,6 +107,15 @@ func (c *discoveryConfigServiceClient) UpdateDiscoveryConfig(ctx context.Context return out, nil } +func (c *discoveryConfigServiceClient) UpsertDiscoveryConfig(ctx context.Context, in *UpsertDiscoveryConfigRequest, opts ...grpc.CallOption) (*DiscoveryConfig, error) { + out := new(DiscoveryConfig) + err := c.cc.Invoke(ctx, DiscoveryConfigService_UpsertDiscoveryConfig_FullMethodName, in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *discoveryConfigServiceClient) DeleteDiscoveryConfig(ctx context.Context, in *DeleteDiscoveryConfigRequest, opts ...grpc.CallOption) (*emptypb.Empty, error) { out := new(emptypb.Empty) err := c.cc.Invoke(ctx, DiscoveryConfigService_DeleteDiscoveryConfig_FullMethodName, in, out, opts...) @@ -134,6 +146,8 @@ type DiscoveryConfigServiceServer interface { CreateDiscoveryConfig(context.Context, *CreateDiscoveryConfigRequest) (*DiscoveryConfig, error) // UpdateDiscoveryConfig updates an existing DiscoveryConfig resource. UpdateDiscoveryConfig(context.Context, *UpdateDiscoveryConfigRequest) (*DiscoveryConfig, error) + // UpsertDiscoveryConfig creates or updates a DiscoveryConfig resource. + UpsertDiscoveryConfig(context.Context, *UpsertDiscoveryConfigRequest) (*DiscoveryConfig, error) // DeleteDiscoveryConfig removes the specified DiscoveryConfig resource. DeleteDiscoveryConfig(context.Context, *DeleteDiscoveryConfigRequest) (*emptypb.Empty, error) // DeleteAllDiscoveryConfigs removes all DiscoveryConfigs. @@ -157,6 +171,9 @@ func (UnimplementedDiscoveryConfigServiceServer) CreateDiscoveryConfig(context.C func (UnimplementedDiscoveryConfigServiceServer) UpdateDiscoveryConfig(context.Context, *UpdateDiscoveryConfigRequest) (*DiscoveryConfig, error) { return nil, status.Errorf(codes.Unimplemented, "method UpdateDiscoveryConfig not implemented") } +func (UnimplementedDiscoveryConfigServiceServer) UpsertDiscoveryConfig(context.Context, *UpsertDiscoveryConfigRequest) (*DiscoveryConfig, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpsertDiscoveryConfig not implemented") +} func (UnimplementedDiscoveryConfigServiceServer) DeleteDiscoveryConfig(context.Context, *DeleteDiscoveryConfigRequest) (*emptypb.Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method DeleteDiscoveryConfig not implemented") } @@ -249,6 +266,24 @@ func _DiscoveryConfigService_UpdateDiscoveryConfig_Handler(srv interface{}, ctx return interceptor(ctx, in, info, handler) } +func _DiscoveryConfigService_UpsertDiscoveryConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpsertDiscoveryConfigRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DiscoveryConfigServiceServer).UpsertDiscoveryConfig(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: DiscoveryConfigService_UpsertDiscoveryConfig_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DiscoveryConfigServiceServer).UpsertDiscoveryConfig(ctx, req.(*UpsertDiscoveryConfigRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _DiscoveryConfigService_DeleteDiscoveryConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(DeleteDiscoveryConfigRequest) if err := dec(in); err != nil { @@ -308,6 +343,10 @@ var DiscoveryConfigService_ServiceDesc = grpc.ServiceDesc{ MethodName: "UpdateDiscoveryConfig", Handler: _DiscoveryConfigService_UpdateDiscoveryConfig_Handler, }, + { + MethodName: "UpsertDiscoveryConfig", + Handler: _DiscoveryConfigService_UpsertDiscoveryConfig_Handler, + }, { MethodName: "DeleteDiscoveryConfig", Handler: _DiscoveryConfigService_DeleteDiscoveryConfig_Handler, diff --git a/api/proto/teleport/discoveryconfig/v1/discoveryconfig_service.proto b/api/proto/teleport/discoveryconfig/v1/discoveryconfig_service.proto index 22e4e6b7032e6..d4c285c2eb4f4 100644 --- a/api/proto/teleport/discoveryconfig/v1/discoveryconfig_service.proto +++ b/api/proto/teleport/discoveryconfig/v1/discoveryconfig_service.proto @@ -39,6 +39,9 @@ service DiscoveryConfigService { // UpdateDiscoveryConfig updates an existing DiscoveryConfig resource. rpc UpdateDiscoveryConfig(UpdateDiscoveryConfigRequest) returns (DiscoveryConfig); + // UpsertDiscoveryConfig creates or updates a DiscoveryConfig resource. + rpc UpsertDiscoveryConfig(UpsertDiscoveryConfigRequest) returns (DiscoveryConfig); + // DeleteDiscoveryConfig removes the specified DiscoveryConfig resource. rpc DeleteDiscoveryConfig(DeleteDiscoveryConfigRequest) returns (google.protobuf.Empty); @@ -83,6 +86,12 @@ message UpdateDiscoveryConfigRequest { DiscoveryConfig discovery_config = 1; } +// UpsertDiscoveryConfigRequest is the request to upsert the provided DiscoveryConfig. +message UpsertDiscoveryConfigRequest { + // DiscoveryConfig is the DiscoveryConfig to be upserted. + DiscoveryConfig discovery_config = 1; +} + // DeleteDiscoveryConfigRequest is a request for deleting a specific DiscoveryConfig resource. message DeleteDiscoveryConfigRequest { // Name is the name of the DiscoveryConfig to be deleted. diff --git a/lib/auth/auth.go b/lib/auth/auth.go index c06ed248f8ea8..73d6aefd23c07 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -254,6 +254,12 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { return nil, trace.Wrap(err) } } + if cfg.DiscoveryConfigs == nil { + cfg.DiscoveryConfigs, err = local.NewDiscoveryConfigService(cfg.Backend) + if err != nil { + return nil, trace.Wrap(err) + } + } if cfg.Embeddings == nil { cfg.Embeddings = local.NewEmbeddingsService(cfg.Backend) } @@ -315,6 +321,7 @@ func NewServer(cfg *InitConfig, opts ...ServerOption) (*Server, error) { SessionTrackerService: cfg.SessionTrackerService, ConnectionsDiagnostic: cfg.ConnectionsDiagnostic, Integrations: cfg.Integrations, + DiscoveryConfigs: cfg.DiscoveryConfigs, Embeddings: cfg.Embeddings, Okta: cfg.Okta, AccessLists: cfg.AccessLists, @@ -451,6 +458,7 @@ type Services struct { services.ConnectionsDiagnostic services.StatusInternal services.Integrations + services.DiscoveryConfigs services.Okta services.AccessLists services.UserLoginStates @@ -484,6 +492,11 @@ func (r *Services) AccessListClient() services.AccessLists { return r } +// DiscoveryConfigClient returns the DiscoveryConfig client. +func (r *Services) DiscoveryConfigClient() services.DiscoveryConfigs { + return r +} + // UserLoginStateClient returns the user login state client. func (r *Services) UserLoginStateClient() services.UserLoginStates { return r diff --git a/lib/auth/auth_with_roles.go b/lib/auth/auth_with_roles.go index fd9ba2aa12b2f..2bef73aedc3b3 100644 --- a/lib/auth/auth_with_roles.go +++ b/lib/auth/auth_with_roles.go @@ -36,6 +36,7 @@ import ( "github.com/gravitational/teleport/api" "github.com/gravitational/teleport/api/client" "github.com/gravitational/teleport/api/client/accesslist" + "github.com/gravitational/teleport/api/client/discoveryconfig" "github.com/gravitational/teleport/api/client/okta" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/client/userloginstate" @@ -44,6 +45,7 @@ import ( "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" accesslistv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/accesslist/v1" devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" + discoveryconfigv1 "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1" integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" oktapb "github.com/gravitational/teleport/api/gen/proto/go/teleport/okta/v1" @@ -347,6 +349,14 @@ func (a *ServerWithRoles) AccessListClient() services.AccessLists { utils.NewGRPCDummyClientConnection("AccessListClient() should not be called on ServerWithRoles"))) } +// DiscoveryConfigClient allows ServerWithRoles to implement ClientI. +// It should not be called through ServerWithRoles, +// as it returns a dummy client that will always respond with "not implemented". +func (a *ServerWithRoles) DiscoveryConfigClient() services.DiscoveryConfigs { + return discoveryconfig.NewClient(discoveryconfigv1.NewDiscoveryConfigServiceClient( + utils.NewGRPCDummyClientConnection("DiscoveryConfigClient() should not be called on ServerWithRoles"))) +} + // ResourceUsageClient allows ServerWithRoles to implement ClientI. // It should not be called through ServerWithRoles, // as it returns a dummy client that will always respond with "not implemented". diff --git a/lib/auth/clt.go b/lib/auth/clt.go index eb176f5c3c85d..8c610c0ddbe2f 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -466,6 +466,11 @@ func (c *Client) UpsertUser(ctx context.Context, user types.User) (types.User, e return upserted, trace.Wrap(err) } +// DiscoveryConfigClient returns a client for managing the DiscoveryConfig resource. +func (c *Client) DiscoveryConfigClient() services.DiscoveryConfigs { + return c.APIClient.DiscoveryConfigClient() +} + // WebService implements features used by Web UI clients type WebService interface { // GetWebSessionInfo checks if a web session is valid, returns session id in case if @@ -866,6 +871,12 @@ type ClientI interface { // (as per the default gRPC behavior). UserLoginStateClient() services.UserLoginStates + // DiscoveryConfigClient returns a DiscoveryConfig client. + // Clients connecting to older Teleport versions, still get an DiscoveryConfig client + // when calling this method, but all RPCs will return "not implemented" errors + // (as per the default gRPC behavior). + DiscoveryConfigClient() services.DiscoveryConfigs + // ResourceUsageClient returns a resource usage service client. // Clients connecting to non-Enterprise clusters, or older Teleport versions, // still get a client when calling this method, but all RPCs will return diff --git a/lib/auth/discoveryconfig/discoveryconfigv1/service.go b/lib/auth/discoveryconfig/discoveryconfigv1/service.go index 00092cd0c2152..860db0a43fe15 100644 --- a/lib/auth/discoveryconfig/discoveryconfigv1/service.go +++ b/lib/auth/discoveryconfig/discoveryconfigv1/service.go @@ -170,6 +170,26 @@ func (s *Service) UpdateDiscoveryConfig(ctx context.Context, req *discoveryconfi return conv.ToProto(resp), nil } +// UpsertDiscoveryConfig creates or updates a DiscoveryConfig. +func (s *Service) UpsertDiscoveryConfig(ctx context.Context, req *discoveryconfigv1.UpsertDiscoveryConfigRequest) (*discoveryconfigv1.DiscoveryConfig, error) { + _, err := authz.AuthorizeWithVerbs(ctx, s.log, s.authorizer, true, types.KindDiscoveryConfig, types.VerbCreate, types.VerbUpdate) + if err != nil { + return nil, trace.Wrap(err) + } + + dc, err := conv.FromProto(req.GetDiscoveryConfig()) + if err != nil { + return nil, trace.Wrap(err) + } + + resp, err := s.backend.UpsertDiscoveryConfig(ctx, dc) + if err != nil { + return nil, trace.Wrap(err) + } + + return conv.ToProto(resp), nil +} + // DeleteDiscoveryConfig removes the specified DiscoveryConfig resource. func (s *Service) DeleteDiscoveryConfig(ctx context.Context, req *discoveryconfigv1.DeleteDiscoveryConfigRequest) (*emptypb.Empty, error) { _, err := authz.AuthorizeWithVerbs(ctx, s.log, s.authorizer, true, types.KindDiscoveryConfig, types.VerbDelete) diff --git a/lib/auth/discoveryconfig/discoveryconfigv1/service_test.go b/lib/auth/discoveryconfig/discoveryconfigv1/service_test.go index c2013654b04eb..6dbd489f301a3 100644 --- a/lib/auth/discoveryconfig/discoveryconfigv1/service_test.go +++ b/lib/auth/discoveryconfig/discoveryconfigv1/service_test.go @@ -223,6 +223,43 @@ func TestDiscoveryConfigCRUD(t *testing.T) { ErrAssertion: require.NoError, }, + // Upsert + { + Name: "no access to upsert discovery config", + Role: types.RoleSpecV6{ + Allow: types.RoleConditions{Rules: []types.Rule{{ + Resources: []string{types.KindDiscoveryConfig}, + Verbs: []string{types.VerbUpdate}, // missing VerbCreate + }}}, + }, + Test: func(ctx context.Context, resourceSvc *Service, dcName string) error { + dc := sampleDiscoveryConfigFn(t, dcName) + _, err := resourceSvc.UpsertDiscoveryConfig(ctx, &discoveryconfigpb.UpsertDiscoveryConfigRequest{ + DiscoveryConfig: convert.ToProto(dc), + }) + return err + }, + ErrAssertion: requireTraceErrorFn(trace.IsAccessDenied), + }, + { + Name: "access to upsert discovery config", + Role: types.RoleSpecV6{ + Allow: types.RoleConditions{Rules: []types.Rule{{ + Resources: []string{types.KindDiscoveryConfig}, + Verbs: []string{types.VerbUpdate, types.VerbCreate}, + }}}, + }, + Setup: func(t *testing.T, dcName string) {}, + Test: func(ctx context.Context, resourceSvc *Service, dcName string) error { + dc := sampleDiscoveryConfigFn(t, dcName) + _, err := resourceSvc.UpsertDiscoveryConfig(ctx, &discoveryconfigpb.UpsertDiscoveryConfigRequest{ + DiscoveryConfig: convert.ToProto(dc), + }) + return err + }, + ErrAssertion: require.NoError, + }, + // Delete { Name: "no access to delete discovery config", diff --git a/lib/auth/grpcserver.go b/lib/auth/grpcserver.go index 31bfdffa546b4..463680abe80c3 100644 --- a/lib/auth/grpcserver.go +++ b/lib/auth/grpcserver.go @@ -47,6 +47,7 @@ import ( "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/gen/proto/go/assist/v1" auditlogpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/auditlog/v1" + discoveryconfigpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/discoveryconfig/v1" integrationpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/integration/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" oktapb "github.com/gravitational/teleport/api/gen/proto/go/teleport/okta/v1" @@ -59,6 +60,7 @@ import ( "github.com/gravitational/teleport/api/types/installers" "github.com/gravitational/teleport/api/types/wrappers" "github.com/gravitational/teleport/lib/auth/assist/assistv1" + "github.com/gravitational/teleport/lib/auth/discoveryconfig/discoveryconfigv1" integrationService "github.com/gravitational/teleport/lib/auth/integration/integrationv1" "github.com/gravitational/teleport/lib/auth/loginrule" "github.com/gravitational/teleport/lib/auth/okta" @@ -5449,6 +5451,16 @@ func NewGRPCServer(cfg GRPCServerConfig) (*GRPCServer, error) { } integrationpb.RegisterIntegrationServiceServer(server, integrationServiceServer) + discoveryConfig, err := discoveryconfigv1.NewService(discoveryconfigv1.ServiceConfig{ + Authorizer: cfg.Authorizer, + Backend: cfg.AuthServer.Services, + Clock: cfg.AuthServer.clock, + }) + if err != nil { + return nil, trace.Wrap(err) + } + discoveryconfigpb.RegisterDiscoveryConfigServiceServer(server, discoveryConfig) + // Initialize and register the user preferences service. userPreferencesSrv, err := userpreferencesv1.NewService(&userpreferencesv1.ServiceConfig{ Backend: cfg.AuthServer.Services, diff --git a/lib/auth/init.go b/lib/auth/init.go index 841c05caa7af9..6b328790311e7 100644 --- a/lib/auth/init.go +++ b/lib/auth/init.go @@ -206,6 +206,9 @@ type InitConfig struct { // Integrations is a service that manages Integrations. Integrations services.Integrations + // DiscoveryConfigs is a service that manages DiscoveryConfigs. + DiscoveryConfigs services.DiscoveryConfigs + // Embeddings is a service that manages Embeddings Embeddings services.Embeddings diff --git a/lib/authz/permissions.go b/lib/authz/permissions.go index f8da11843eef7..9b552676ba6c7 100644 --- a/lib/authz/permissions.go +++ b/lib/authz/permissions.go @@ -887,6 +887,7 @@ func definitionForBuiltinRole(clusterName string, recConfig types.SessionRecordi types.NewRule(types.KindDatabase, services.RW()), types.NewRule(types.KindServerInfo, services.RW()), types.NewRule(types.KindApp, services.RW()), + types.NewRule(types.KindDiscoveryConfig, services.RO()), }, // Discovery service should only access kubes/apps/dbs that originated from discovery. KubernetesLabels: types.Labels{types.OriginLabel: []string{types.OriginCloud}}, diff --git a/lib/cache/cache.go b/lib/cache/cache.go index 9cb4c5d8aa1e1..0449a4ab95199 100644 --- a/lib/cache/cache.go +++ b/lib/cache/cache.go @@ -37,6 +37,7 @@ import ( apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" + "github.com/gravitational/teleport/api/types/discoveryconfig" "github.com/gravitational/teleport/api/types/userloginstate" "github.com/gravitational/teleport/api/utils/retryutils" "github.com/gravitational/teleport/lib/backend" @@ -119,6 +120,7 @@ func ForAuth(cfg Config) Config { {Kind: types.KindAccessList}, {Kind: types.KindUserLoginState}, {Kind: types.KindAccessListMember}, + {Kind: types.KindDiscoveryConfig}, } cfg.QueueSize = defaults.AuthQueueSize // We don't want to enable partial health for auth cache because auth uses an event stream @@ -368,6 +370,7 @@ func ForDiscovery(cfg Config) Config { {Kind: types.KindKubernetesCluster}, {Kind: types.KindDatabase}, {Kind: types.KindApp}, + {Kind: types.KindDiscoveryConfig}, } cfg.QueueSize = defaults.DiscoveryQueueSize return cfg @@ -474,6 +477,7 @@ type Cache struct { userGroupsCache services.UserGroups oktaCache services.Okta integrationsCache services.Integrations + discoveryConfigsCache services.DiscoveryConfigs headlessAuthenticationsCache services.HeadlessAuthenticationService accessListsCache services.AccessLists userLoginStateCache services.UserLoginStates @@ -630,6 +634,8 @@ type Config struct { Okta services.Okta // Integrations is an Integrations service. Integrations services.Integrations + // DiscoveryConfigs is a DiscoveryConfigs service. + DiscoveryConfigs services.DiscoveryConfigs // AccessLists is the access list service. AccessLists services.AccessLists // UserLoginStates is the user login state service. @@ -803,6 +809,12 @@ func New(config Config) (*Cache, error) { return nil, trace.Wrap(err) } + discoveryConfigsCache, err := local.NewDiscoveryConfigService(config.Backend) + if err != nil { + cancel() + return nil, trace.Wrap(err) + } + accessListsCache, err := local.NewAccessListService(config.Backend, config.Clock) if err != nil { cancel() @@ -843,6 +855,7 @@ func New(config Config) (*Cache, error) { userGroupsCache: userGroupsCache, oktaCache: oktaCache, integrationsCache: integrationsCache, + discoveryConfigsCache: discoveryConfigsCache, headlessAuthenticationsCache: local.NewIdentityService(config.Backend), accessListsCache: accessListsCache, userLoginStateCache: userLoginStatesCache, @@ -2479,6 +2492,32 @@ func (c *Cache) GetIntegration(ctx context.Context, name string) (types.Integrat return rg.reader.GetIntegration(ctx, name) } +// ListDiscoveryConfigs returns a paginated list of all DiscoveryConfig resources. +func (c *Cache) ListDiscoveryConfigs(ctx context.Context, pageSize int, nextKey string) ([]*discoveryconfig.DiscoveryConfig, string, error) { + ctx, span := c.Tracer.Start(ctx, "cache/ListDiscoveryConfigs") + defer span.End() + + rg, err := readCollectionCache(c, c.collections.discoveryConfigs) + if err != nil { + return nil, "", trace.Wrap(err) + } + defer rg.Release() + return rg.reader.ListDiscoveryConfigs(ctx, pageSize, nextKey) +} + +// GetDiscoveryConfig returns the specified DiscoveryConfig resource. +func (c *Cache) GetDiscoveryConfig(ctx context.Context, name string) (*discoveryconfig.DiscoveryConfig, error) { + ctx, span := c.Tracer.Start(ctx, "cache/GetDiscoveryConfig") + defer span.End() + + rg, err := readCollectionCache(c, c.collections.discoveryConfigs) + if err != nil { + return nil, trace.Wrap(err) + } + defer rg.Release() + return rg.reader.GetDiscoveryConfig(ctx, name) +} + // ListAccessLists returns a paginated list of all access lists resources. func (c *Cache) ListAccessLists(ctx context.Context, pageSize int, nextKey string) ([]*accesslist.AccessList, string, error) { ctx, span := c.Tracer.Start(ctx, "cache/ListAccessLists") diff --git a/lib/cache/cache_test.go b/lib/cache/cache_test.go index 041b40ad66267..3db8e46cb05ff 100644 --- a/lib/cache/cache_test.go +++ b/lib/cache/cache_test.go @@ -39,6 +39,7 @@ import ( apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" + "github.com/gravitational/teleport/api/types/discoveryconfig" "github.com/gravitational/teleport/api/types/header" "github.com/gravitational/teleport/api/types/trait" "github.com/gravitational/teleport/api/types/userloginstate" @@ -92,6 +93,7 @@ type testPack struct { userGroups services.UserGroups okta services.Okta integrations services.Integrations + discoveryConfigs services.DiscoveryConfigs accessLists services.AccessLists userLoginStates services.UserLoginStates accessListMembers services.AccessListMembers @@ -246,6 +248,12 @@ func newPackWithoutCache(dir string, opts ...packOption) (*testPack, error) { } p.integrations = igSvc + dcSvc, err := local.NewDiscoveryConfigService(p.backend) + if err != nil { + return nil, trace.Wrap(err) + } + p.discoveryConfigs = dcSvc + alSvc, err := local.NewAccessListService(p.backend, p.backend.Clock()) if err != nil { return nil, trace.Wrap(err) @@ -296,6 +304,7 @@ func newPack(dir string, setupConfig func(c Config) Config, opts ...packOption) UserGroups: p.userGroups, Okta: p.okta, Integrations: p.integrations, + DiscoveryConfigs: p.discoveryConfigs, AccessLists: p.accessLists, UserLoginStates: p.userLoginStates, MaxRetryPeriod: 200 * time.Millisecond, @@ -692,6 +701,7 @@ func TestCompletenessInit(t *testing.T) { UserGroups: p.userGroups, Okta: p.okta, Integrations: p.integrations, + DiscoveryConfigs: p.discoveryConfigs, AccessLists: p.accessLists, UserLoginStates: p.userLoginStates, MaxRetryPeriod: 200 * time.Millisecond, @@ -762,6 +772,7 @@ func TestCompletenessReset(t *testing.T) { UserGroups: p.userGroups, Okta: p.okta, Integrations: p.integrations, + DiscoveryConfigs: p.discoveryConfigs, AccessLists: p.accessLists, UserLoginStates: p.userLoginStates, MaxRetryPeriod: 200 * time.Millisecond, @@ -944,6 +955,7 @@ func TestListResources_NodesTTLVariant(t *testing.T) { UserGroups: p.userGroups, Okta: p.okta, Integrations: p.integrations, + DiscoveryConfigs: p.discoveryConfigs, AccessLists: p.accessLists, UserLoginStates: p.userLoginStates, MaxRetryPeriod: 200 * time.Millisecond, @@ -1025,6 +1037,7 @@ func initStrategy(t *testing.T) { UserGroups: p.userGroups, Okta: p.okta, Integrations: p.integrations, + DiscoveryConfigs: p.discoveryConfigs, AccessLists: p.accessLists, UserLoginStates: p.userLoginStates, MaxRetryPeriod: 200 * time.Millisecond, @@ -2090,6 +2103,45 @@ func TestIntegrations(t *testing.T) { }) } +// TestDiscoveryConfig tests that CRUD operations on DiscoveryConfig resources are +// replicated from the backend to the cache. +func TestDiscoveryConfig(t *testing.T) { + t.Parallel() + + p := newTestPack(t, ForAuth) + t.Cleanup(p.Close) + + testResources(t, p, testFuncs[*discoveryconfig.DiscoveryConfig]{ + newResource: func(name string) (*discoveryconfig.DiscoveryConfig, error) { + dc, err := discoveryconfig.NewDiscoveryConfig( + header.Metadata{Name: "mydc"}, + discoveryconfig.Spec{ + DiscoveryGroup: "group001", + }) + require.NoError(t, err) + return dc, nil + }, + create: func(ctx context.Context, discoveryConfig *discoveryconfig.DiscoveryConfig) error { + _, err := p.discoveryConfigs.CreateDiscoveryConfig(ctx, discoveryConfig) + return trace.Wrap(err) + }, + list: func(ctx context.Context) ([]*discoveryconfig.DiscoveryConfig, error) { + results, _, err := p.discoveryConfigs.ListDiscoveryConfigs(ctx, 0, "") + return results, err + }, + cacheGet: p.cache.GetDiscoveryConfig, + cacheList: func(ctx context.Context) ([]*discoveryconfig.DiscoveryConfig, error) { + results, _, err := p.cache.ListDiscoveryConfigs(ctx, 0, "") + return results, err + }, + update: func(ctx context.Context, discoveryConfig *discoveryconfig.DiscoveryConfig) error { + _, err := p.discoveryConfigs.UpdateDiscoveryConfig(ctx, discoveryConfig) + return trace.Wrap(err) + }, + deleteAll: p.discoveryConfigs.DeleteAllDiscoveryConfigs, + }) +} + // TestAccessLists tests that CRUD operations on access list resources are // replicated from the backend to the cache. func TestAccessLists(t *testing.T) { @@ -2639,6 +2691,7 @@ func TestCacheWatchKindExistsInEvents(t *testing.T) { types.KindOktaImportRule: &types.OktaImportRuleV1{}, types.KindOktaAssignment: &types.OktaAssignmentV1{}, types.KindIntegration: &types.IntegrationV1{}, + types.KindDiscoveryConfig: newDiscoveryConfig(t, "discovery-config"), types.KindHeadlessAuthentication: &types.HeadlessAuthentication{}, types.KindAccessList: newAccessList(t, "access-list", clock), types.KindUserLoginState: newUserLoginState(t, "user-login-state"), @@ -2895,6 +2948,25 @@ func newAccessList(t *testing.T, name string, clock clockwork.Clock) *accesslist return accessList } +func newDiscoveryConfig(t *testing.T, name string) *discoveryconfig.DiscoveryConfig { + t.Helper() + + discoveryConfig, err := discoveryconfig.NewDiscoveryConfig( + header.Metadata{ + Name: name, + }, + discoveryconfig.Spec{ + DiscoveryGroup: "mygroup", + AWS: []types.AWSMatcher{}, + Azure: []types.AzureMatcher{}, + GCP: []types.GCPMatcher{}, + Kube: []types.KubernetesMatcher{}, + }, + ) + require.NoError(t, err) + return discoveryConfig +} + func newUserLoginState(t *testing.T, name string) *userloginstate.UserLoginState { t.Helper() diff --git a/lib/cache/collections.go b/lib/cache/collections.go index f64b5c64c4109..e3a481474818a 100644 --- a/lib/cache/collections.go +++ b/lib/cache/collections.go @@ -28,6 +28,7 @@ import ( apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/types/accesslist" + "github.com/gravitational/teleport/api/types/discoveryconfig" "github.com/gravitational/teleport/api/types/userloginstate" "github.com/gravitational/teleport/lib/services" ) @@ -187,6 +188,7 @@ type cacheCollections struct { clusterNetworkingConfigs collectionReader[clusterNetworkingConfigGetter] databases collectionReader[services.DatabaseGetter] databaseServers collectionReader[databaseServerGetter] + discoveryConfigs collectionReader[services.DiscoveryConfigsGetter] installers collectionReader[installerGetter] integrations collectionReader[services.IntegrationsGetter] kubeClusters collectionReader[kubernetesClusterGetter] @@ -595,6 +597,12 @@ func setupCollections(c *Cache, watches []types.WatchKind) (*cacheCollections, e watch: watch, } collections.byKind[resourceKind] = collections.integrations + case types.KindDiscoveryConfig: + if c.DiscoveryConfigs == nil { + return nil, trace.BadParameter("missing parameter DiscoveryConfigs") + } + collections.discoveryConfigs = &genericCollection[*discoveryconfig.DiscoveryConfig, services.DiscoveryConfigsGetter, discoveryConfigExecutor]{cache: c, watch: watch} + collections.byKind[resourceKind] = collections.discoveryConfigs case types.KindHeadlessAuthentication: // For headless authentications, we need only process events. We don't need to keep the cache up to date. collections.byKind[resourceKind] = &genericCollection[*types.HeadlessAuthentication, noReader, noopExecutor]{cache: c, watch: watch} @@ -2374,6 +2382,53 @@ func (accessListsExecutor) getReader(cache *Cache, cacheOK bool) services.Access var _ executor[*accesslist.AccessList, services.AccessListsGetter] = accessListsExecutor{} +type discoveryConfigExecutor struct{} + +func (discoveryConfigExecutor) getAll(ctx context.Context, cache *Cache, loadSecrets bool) ([]*discoveryconfig.DiscoveryConfig, error) { + var discoveryConfigs []*discoveryconfig.DiscoveryConfig + var nextToken string + for { + var page []*discoveryconfig.DiscoveryConfig + var err error + + page, nextToken, err = cache.DiscoveryConfigs.ListDiscoveryConfigs(ctx, 0 /* default page size */, nextToken) + if err != nil { + return nil, trace.Wrap(err) + } + + discoveryConfigs = append(discoveryConfigs, page...) + + if nextToken == "" { + break + } + } + return discoveryConfigs, nil +} + +func (discoveryConfigExecutor) upsert(ctx context.Context, cache *Cache, resource *discoveryconfig.DiscoveryConfig) error { + _, err := cache.discoveryConfigsCache.UpsertDiscoveryConfig(ctx, resource) + return trace.Wrap(err) +} + +func (discoveryConfigExecutor) deleteAll(ctx context.Context, cache *Cache) error { + return cache.discoveryConfigsCache.DeleteAllDiscoveryConfigs(ctx) +} + +func (discoveryConfigExecutor) delete(ctx context.Context, cache *Cache, resource types.Resource) error { + return cache.discoveryConfigsCache.DeleteDiscoveryConfig(ctx, resource.GetName()) +} + +func (discoveryConfigExecutor) isSingleton() bool { return false } + +func (discoveryConfigExecutor) getReader(cache *Cache, cacheOK bool) services.DiscoveryConfigsGetter { + if cacheOK { + return cache.discoveryConfigsCache + } + return cache.Config.DiscoveryConfigs +} + +var _ executor[*discoveryconfig.DiscoveryConfig, services.DiscoveryConfigsGetter] = discoveryConfigExecutor{} + // noopExecutor can be used when a resource's events do not need to processed by // the cache itself, only passed on to other watchers. type noopExecutor struct{} diff --git a/lib/service/service.go b/lib/service/service.go index 5f136a79cad5f..dbba681e28ec2 100644 --- a/lib/service/service.go +++ b/lib/service/service.go @@ -2226,6 +2226,7 @@ func (process *TeleportProcess) newAccessCache(cfg accessCacheConfig) (*cache.Ca AccessLists: cfg.services.AccessListClient(), UserLoginStates: cfg.services.UserLoginStateClient(), Integrations: cfg.services, + DiscoveryConfigs: cfg.services.DiscoveryConfigClient(), WebSession: cfg.services.WebSessions(), WebToken: cfg.services.WebTokens(), Component: teleport.Component(append(cfg.cacheName, process.id, teleport.ComponentCache)...), diff --git a/lib/services/discoveryconfig.go b/lib/services/discoveryconfig.go index 00bdde2b59a89..7e5d4fcd3990c 100644 --- a/lib/services/discoveryconfig.go +++ b/lib/services/discoveryconfig.go @@ -35,6 +35,8 @@ type DiscoveryConfigs interface { CreateDiscoveryConfig(context.Context, *discoveryconfig.DiscoveryConfig) (*discoveryconfig.DiscoveryConfig, error) // UpdateDiscoveryConfig updates an existing DiscoveryConfig resource. UpdateDiscoveryConfig(context.Context, *discoveryconfig.DiscoveryConfig) (*discoveryconfig.DiscoveryConfig, error) + // UpsertDiscoveryConfig upserts a DiscoveryConfig resource. + UpsertDiscoveryConfig(context.Context, *discoveryconfig.DiscoveryConfig) (*discoveryconfig.DiscoveryConfig, error) // DeleteDiscoveryConfig removes the specified DiscoveryConfig resource. DeleteDiscoveryConfig(ctx context.Context, name string) error // DeleteAllDiscoveryConfigs removes all DiscoveryConfigs. diff --git a/lib/services/local/discoveryconfig.go b/lib/services/local/discoveryconfig.go index 218312693f75d..086a5f926f87b 100644 --- a/lib/services/local/discoveryconfig.go +++ b/lib/services/local/discoveryconfig.go @@ -103,6 +103,19 @@ func (s *DiscoveryConfigService) UpdateDiscoveryConfig(ctx context.Context, dc * return dc, nil } +// UpsertDiscoveryConfigs upserts a DiscoveryConfig resource. +func (s *DiscoveryConfigService) UpsertDiscoveryConfig(ctx context.Context, dc *discoveryconfig.DiscoveryConfig) (*discoveryconfig.DiscoveryConfig, error) { + if err := dc.CheckAndSetDefaults(); err != nil { + return nil, trace.Wrap(err) + } + + if err := s.svc.UpsertResource(ctx, dc); err != nil { + return nil, trace.Wrap(err) + } + + return dc, nil +} + // DeleteDiscoveryConfigs removes the specified DiscoveryConfig resource. func (s *DiscoveryConfigService) DeleteDiscoveryConfig(ctx context.Context, name string) error { return trace.Wrap(s.svc.DeleteResource(ctx, name)) diff --git a/lib/services/local/discoveryconfig_test.go b/lib/services/local/discoveryconfig_test.go index ba065f9d1ab93..156d3dca78b0d 100644 --- a/lib/services/local/discoveryconfig_test.go +++ b/lib/services/local/discoveryconfig_test.go @@ -50,6 +50,7 @@ func TestDiscoveryConfigCRUD(t *testing.T) { // Create a couple discovery configs. discoveryConfig1 := newDiscoveryConfig(t, "discovery-config-1") discoveryConfig2 := newDiscoveryConfig(t, "discovery-config-2") + discoveryConfig3 := newDiscoveryConfig(t, "discovery-config-3") // Initially we expect no discovery configs. out, nextToken, err := service.ListDiscoveryConfigs(ctx, 0, "") @@ -102,13 +103,27 @@ func TestDiscoveryConfigCRUD(t *testing.T) { require.NoError(t, err) require.Empty(t, cmp.Diff(discoveryConfig1, discoveryConfig, cmpOpts...)) + // Upsert a discovery config updates if it already exists. + discoveryConfig1.SetExpiry(clock.Now().Add(40 * time.Minute)) + discoveryConfig, err = service.UpsertDiscoveryConfig(ctx, discoveryConfig1) + require.NoError(t, err) + require.Empty(t, cmp.Diff(discoveryConfig1, discoveryConfig, cmpOpts...)) + discoveryConfig, err = service.GetDiscoveryConfig(ctx, discoveryConfig1.GetName()) + require.NoError(t, err) + require.Empty(t, cmp.Diff(discoveryConfig1, discoveryConfig, cmpOpts...)) + + // Upsert a discovery config creates if it doesn't exist. + discoveryConfig, err = service.UpsertDiscoveryConfig(ctx, discoveryConfig3) + require.NoError(t, err) + require.Empty(t, cmp.Diff(discoveryConfig3, discoveryConfig, cmpOpts...)) + // Delete a discovery config. err = service.DeleteDiscoveryConfig(ctx, discoveryConfig1.GetName()) require.NoError(t, err) out, nextToken, err = service.ListDiscoveryConfigs(ctx, 0, "") require.NoError(t, err) require.Empty(t, nextToken) - require.Empty(t, cmp.Diff([]*discoveryconfig.DiscoveryConfig{discoveryConfig2}, out, cmpOpts...)) + require.Empty(t, cmp.Diff([]*discoveryconfig.DiscoveryConfig{discoveryConfig2, discoveryConfig3}, out, cmpOpts...)) // Try to delete a discovery config that doesn't exist. err = service.DeleteDiscoveryConfig(ctx, "doesnotexist") diff --git a/lib/services/local/events.go b/lib/services/local/events.go index e3cb6394d9935..5fd2eecbc20de 100644 --- a/lib/services/local/events.go +++ b/lib/services/local/events.go @@ -162,6 +162,8 @@ func (e *EventsService) NewWatcher(ctx context.Context, watch types.Watch) (type parser = newOktaAssignmentParser() case types.KindIntegration: parser = newIntegrationParser() + case types.KindDiscoveryConfig: + parser = newDiscoveryConfigParser() case types.KindHeadlessAuthentication: p, err := newHeadlessAuthenticationParser(kind.Filter) if err != nil { @@ -1609,6 +1611,31 @@ func (p *integrationParser) parse(event backend.Event) (types.Resource, error) { } } +func newDiscoveryConfigParser() *discoveryConfigParser { + return &discoveryConfigParser{ + baseParser: newBaseParser(backend.Key(discoveryConfigPrefix)), + } +} + +type discoveryConfigParser struct { + baseParser +} + +func (p *discoveryConfigParser) parse(event backend.Event) (types.Resource, error) { + switch event.Type { + case types.OpDelete: + return resourceHeader(event, types.KindDiscoveryConfig, types.V1, 0) + case types.OpPut: + return services.UnmarshalDiscoveryConfig(event.Item.Value, + services.WithResourceID(event.Item.ID), + services.WithExpires(event.Item.Expires), + services.WithRevision(event.Item.Revision), + ) + default: + return nil, trace.BadParameter("event %v is not supported", event.Type) + } +} + func newHeadlessAuthenticationParser(m map[string]string) (*headlessAuthenticationParser, error) { var filter types.HeadlessAuthenticationFilter if err := filter.FromMap(m); err != nil { diff --git a/lib/services/presets.go b/lib/services/presets.go index 0e0794a1c6246..83c8d0e56f041 100644 --- a/lib/services/presets.go +++ b/lib/services/presets.go @@ -162,6 +162,7 @@ func NewPresetEditorRole() types.Role { types.NewRule(types.KindClusterAlert, RW()), types.NewRule(types.KindAccessList, RW()), types.NewRule(types.KindNode, RW()), + types.NewRule(types.KindDiscoveryConfig, RW()), }, }, }, diff --git a/lib/services/resource.go b/lib/services/resource.go index 397d8f90b73ae..e8c23639a2c14 100644 --- a/lib/services/resource.go +++ b/lib/services/resource.go @@ -210,6 +210,8 @@ func ParseShortcut(in string) (string, error) { return types.KindIntegration, nil case types.KindAccessList, types.KindAccessList + "s", "accesslist", "accesslists": return types.KindAccessList, nil + case types.KindDiscoveryConfig, types.KindDiscoveryConfig + "s", "discoveryconfig", "discoveryconfigs": + return types.KindDiscoveryConfig, nil } return "", trace.BadParameter("unsupported resource: %q - resources should be expressed as 'type/name', for example 'connector/github'", in) } diff --git a/lib/services/services.go b/lib/services/services.go index 2405f5618658b..2b9d99b9152b1 100644 --- a/lib/services/services.go +++ b/lib/services/services.go @@ -48,6 +48,7 @@ type Services interface { OktaClient() Okta AccessListClient() AccessLists UserLoginStateClient() UserLoginStates + DiscoveryConfigClient() DiscoveryConfigs } // RotationGetter returns the rotation state. diff --git a/lib/services/useracl.go b/lib/services/useracl.go index fc197b9456deb..5b6ce49d81610 100644 --- a/lib/services/useracl.go +++ b/lib/services/useracl.go @@ -92,6 +92,8 @@ type UserACL struct { SAMLIdpServiceProvider ResourceAccess `json:"samlIdpServiceProvider"` // AccessList defines access to access list management. AccessList ResourceAccess `json:"accessList"` + // DiscoveryConfig defines whether the user has access to manage DiscoveryConfigs. + DiscoveryConfig ResourceAccess `json:"discoverConfigs"` } func hasAccess(roleSet RoleSet, ctx *Context, kind string, verbs ...string) bool { @@ -159,6 +161,7 @@ func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, des license := newAccess(userRoles, ctx, types.KindLicense) deviceTrust := newAccess(userRoles, ctx, types.KindDevice) integrationsAccess := newAccess(userRoles, ctx, types.KindIntegration) + discoveryConfigsAccess := newAccess(userRoles, ctx, types.KindDiscoveryConfig) lockAccess := newAccess(userRoles, ctx, types.KindLock) accessListAccess := newAccess(userRoles, ctx, types.KindAccessList) @@ -187,6 +190,7 @@ func NewUserACL(user types.User, userRoles RoleSet, features proto.Features, des License: license, Plugins: pluginsAccess, Integrations: integrationsAccess, + DiscoveryConfig: discoveryConfigsAccess, DeviceTrust: deviceTrust, Locks: lockAccess, Assist: assistAccess, diff --git a/lib/services/useracl_test.go b/lib/services/useracl_test.go index cafeeec12d0d9..3fa526d50a47a 100644 --- a/lib/services/useracl_test.go +++ b/lib/services/useracl_test.go @@ -147,6 +147,7 @@ func TestNewUserACLCloud(t *testing.T) { require.Empty(t, cmp.Diff(userContext.Tokens, allowedRW)) require.Empty(t, cmp.Diff(userContext.Nodes, allowedRW)) require.Empty(t, cmp.Diff(userContext.AccessRequests, allowedRW)) + require.Empty(t, cmp.Diff(userContext.DiscoveryConfig, allowedRW)) require.Equal(t, userContext.Clipboard, true) require.Equal(t, userContext.DesktopSessionRecording, true) diff --git a/tool/tctl/common/collection.go b/tool/tctl/common/collection.go index 3232d6f70e129..745064238e563 100644 --- a/tool/tctl/common/collection.go +++ b/tool/tctl/common/collection.go @@ -30,6 +30,7 @@ import ( devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/discoveryconfig" apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/devicetrust" @@ -1127,6 +1128,30 @@ func (c *deviceCollection) writeText(w io.Writer, verbose bool) error { return trace.Wrap(err) } +type discoveryConfigCollection struct { + discoveryConfigs []*discoveryconfig.DiscoveryConfig +} + +func (c *discoveryConfigCollection) resources() []types.Resource { + resources := make([]types.Resource, len(c.discoveryConfigs)) + for i, dc := range c.discoveryConfigs { + resources[i] = dc + } + return resources +} + +func (c *discoveryConfigCollection) writeText(w io.Writer, verbose bool) error { + t := asciitable.MakeTable([]string{"Name", "Discovery Group"}) + for _, dc := range c.discoveryConfigs { + t.AddRow([]string{ + dc.GetName(), + dc.GetDiscoveryGroup(), + }) + } + _, err := t.AsBuffer().WriteTo(w) + return trace.Wrap(err) +} + type oktaImportRuleCollection struct { importRules []types.OktaImportRule } diff --git a/tool/tctl/common/resource_command.go b/tool/tctl/common/resource_command.go index 68f1b296ba113..d575c83bb5ea8 100644 --- a/tool/tctl/common/resource_command.go +++ b/tool/tctl/common/resource_command.go @@ -41,6 +41,7 @@ import ( devicepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/devicetrust/v1" loginrulepb "github.com/gravitational/teleport/api/gen/proto/go/teleport/loginrule/v1" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/discoveryconfig" "github.com/gravitational/teleport/api/types/installers" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/client" @@ -131,6 +132,7 @@ func (rc *ResourceCommand) Initialize(app *kingpin.Application, config *servicec types.KindIntegration: rc.createIntegration, types.KindWindowsDesktop: rc.createWindowsDesktop, types.KindAccessList: rc.createAccessList, + types.KindDiscoveryConfig: rc.createDiscoveryConfig, } rc.UpdateHandlers = map[ResourceKind]ResourceCreateHandler{ types.KindUser: rc.updateUser, @@ -979,6 +981,31 @@ func (rc *ResourceCommand) createIntegration(ctx context.Context, client auth.Cl return nil } +func (rc *ResourceCommand) createDiscoveryConfig(ctx context.Context, client auth.ClientI, raw services.UnknownResource) error { + discoveryConfig, err := services.UnmarshalDiscoveryConfig(raw.Raw) + if err != nil { + return trace.Wrap(err) + } + + remote := client.DiscoveryConfigClient() + + if rc.force { + if _, err := remote.UpsertDiscoveryConfig(ctx, discoveryConfig); err != nil { + return trace.Wrap(err) + } + + fmt.Printf("DiscoveryConfig %q has been written\n", discoveryConfig.GetName()) + return nil + } + + if _, err := remote.CreateDiscoveryConfig(ctx, discoveryConfig); err != nil { + return trace.Wrap(err) + } + fmt.Printf("DiscoveryConfig %q has been created\n", discoveryConfig.GetName()) + + return nil +} + func (rc *ResourceCommand) createAccessList(ctx context.Context, client auth.ClientI, raw services.UnknownResource) error { accessList, err := services.UnmarshalAccessList(raw.Raw) if err != nil { @@ -1289,6 +1316,13 @@ func (rc *ResourceCommand) Delete(ctx context.Context, client auth.ClientI) (err } fmt.Printf("Integration %q removed\n", rc.ref.Name) + case types.KindDiscoveryConfig: + remote := client.DiscoveryConfigClient() + if err := remote.DeleteDiscoveryConfig(ctx, rc.ref.Name); err != nil { + return trace.Wrap(err) + } + fmt.Printf("DiscoveryConfig %q removed\n", rc.ref.Name) + case types.KindAppServer: appServers, err := client.GetApplicationServers(ctx, rc.namespace) if err != nil { @@ -2069,6 +2103,33 @@ func (rc *ResourceCommand) getCollection(ctx context.Context, client auth.Client } return &integrationCollection{integrations: resources}, nil + + case types.KindDiscoveryConfig: + remote := client.DiscoveryConfigClient() + if rc.ref.Name != "" { + dc, err := remote.GetDiscoveryConfig(ctx, rc.ref.Name) + if err != nil { + return nil, trace.Wrap(err) + } + return &discoveryConfigCollection{discoveryConfigs: []*discoveryconfig.DiscoveryConfig{dc}}, nil + } + + var resources []*discoveryconfig.DiscoveryConfig + var dcs []*discoveryconfig.DiscoveryConfig + var err error + var nextKey string + for { + dcs, nextKey, err = remote.ListDiscoveryConfigs(ctx, 0, nextKey) + if err != nil { + return nil, trace.Wrap(err) + } + resources = append(resources, dcs...) + if nextKey == "" { + break + } + } + + return &discoveryConfigCollection{discoveryConfigs: resources}, nil } return nil, trace.BadParameter("getting %q is not supported", rc.ref.String()) } diff --git a/tool/tctl/common/resource_command_test.go b/tool/tctl/common/resource_command_test.go index a8615272db0e4..c58b692f08068 100644 --- a/tool/tctl/common/resource_command_test.go +++ b/tool/tctl/common/resource_command_test.go @@ -37,6 +37,8 @@ import ( "github.com/gravitational/teleport/api/constants" apidefaults "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/types/discoveryconfig" + "github.com/gravitational/teleport/api/types/header" "github.com/gravitational/teleport/integration/helpers" "github.com/gravitational/teleport/lib/config" "github.com/gravitational/teleport/lib/defaults" @@ -413,6 +415,119 @@ func TestIntegrationResource(t *testing.T) { }) } +// TestDiscoveryConfigResource tests tctl discoveryConfig commands. +func TestDiscoveryConfigResource(t *testing.T) { + dynAddr := helpers.NewDynamicServiceAddr(t) + + ctx := context.Background() + fileConfig := &config.FileConfig{ + Global: config.Global{ + DataDir: t.TempDir(), + }, + Proxy: config.Proxy{ + Service: config.Service{ + EnabledFlag: "true", + }, + WebAddr: dynAddr.WebAddr, + TunAddr: dynAddr.TunnelAddr, + }, + Auth: config.Auth{ + Service: config.Service{ + EnabledFlag: "true", + ListenAddress: dynAddr.AuthAddr, + }, + }, + } + + auth := makeAndRunTestAuthServer(t, withFileConfig(fileConfig), withFileDescriptors(dynAddr.Descriptors)) + + t.Run("get", func(t *testing.T) { + // Add a lot of DiscoveryConfigs to test pagination + dc, err := discoveryconfig.NewDiscoveryConfig( + header.Metadata{ + Name: "mydiscoveryconfig", + }, + discoveryconfig.Spec{ + DiscoveryGroup: "prod-resources", + }, + ) + require.NoError(t, err) + + randomDiscoveryConfigName := "" + totalDiscoveryConfigs := apidefaults.DefaultChunkSize*2 + 20 // testing partial pages + for i := 0; i < totalDiscoveryConfigs; i++ { + dc.SetName(uuid.NewString()) + if i == apidefaults.DefaultChunkSize { // A "random" discoveryConfig name + randomDiscoveryConfigName = dc.GetName() + } + _, err = auth.GetAuthServer().CreateDiscoveryConfig(ctx, dc) + require.NoError(t, err) + } + + t.Run("test pagination of discovery configs ", func(t *testing.T) { + buff, err := runResourceCommand(t, fileConfig, []string{"get", types.KindDiscoveryConfig, "--format=json"}) + require.NoError(t, err) + out := mustDecodeJSON[[]discoveryconfig.DiscoveryConfig](t, buff) + require.Len(t, out, totalDiscoveryConfigs) + }) + + dcName := fmt.Sprintf("%v/%v", types.KindDiscoveryConfig, randomDiscoveryConfigName) + + t.Run("get specific discovery config", func(t *testing.T) { + buff, err := runResourceCommand(t, fileConfig, []string{"get", dcName, "--format=json"}) + require.NoError(t, err) + out := mustDecodeJSON[[]discoveryconfig.DiscoveryConfig](t, buff) + require.Len(t, out, 1) + require.Equal(t, randomDiscoveryConfigName, out[0].GetName()) + }) + + t.Run("get unknown discovery config", func(t *testing.T) { + unknownDiscoveryConfig := fmt.Sprintf("%v/%v", types.KindDiscoveryConfig, "unknown") + _, err := runResourceCommand(t, fileConfig, []string{"get", unknownDiscoveryConfig, "--format=json"}) + require.True(t, trace.IsNotFound(err), "expected a NotFound error, got %v", err) + }) + + t.Run("get specific discovery config with human output", func(t *testing.T) { + buff, err := runResourceCommand(t, fileConfig, []string{"get", dcName, "--format=text"}) + require.NoError(t, err) + outputString := buff.String() + require.Contains(t, outputString, "prod-resources") + require.Contains(t, outputString, randomDiscoveryConfigName) + }) + }) + + t.Run("create", func(t *testing.T) { + discoveryConfigYAMLPath := filepath.Join(t.TempDir(), "discoveryConfig.yaml") + require.NoError(t, os.WriteFile(discoveryConfigYAMLPath, []byte(discoveryConfigYAML), 0644)) + _, err := runResourceCommand(t, fileConfig, []string{"create", discoveryConfigYAMLPath}) + require.NoError(t, err) + + buff, err := runResourceCommand(t, fileConfig, []string{"get", "discovery_config/my-discovery-config", "--format=text"}) + require.NoError(t, err) + outputString := buff.String() + require.Contains(t, outputString, "my-discovery-config") + require.Contains(t, outputString, "mydg1") + + // Update the discovery group to another group + discoveryConfigYAMLV2 := strings.ReplaceAll(discoveryConfigYAML, "mydg1", "mydg2") + require.NoError(t, os.WriteFile(discoveryConfigYAMLPath, []byte(discoveryConfigYAMLV2), 0644)) + + // Trying to create it again should return an error + _, err = runResourceCommand(t, fileConfig, []string{"create", discoveryConfigYAMLPath}) + require.True(t, trace.IsAlreadyExists(err), "expected already exists error, got %v", err) + + // Using the force should be ok and replace the current object + _, err = runResourceCommand(t, fileConfig, []string{"create", "--force", discoveryConfigYAMLPath}) + require.NoError(t, err) + + // The DiscoveryGroup must be updated + buff, err = runResourceCommand(t, fileConfig, []string{"get", "discovery_config/my-discovery-config", "--format=text"}) + require.NoError(t, err) + outputString = buff.String() + require.Contains(t, outputString, "mydg2") + }) +} + func TestCreateLock(t *testing.T) { dynAddr := helpers.NewDynamicServiceAddr(t) fileConfig := &config.FileConfig{ @@ -621,6 +736,17 @@ spec: aws_oidc: role_arn: "arn:aws:iam::123456789012:role/OpsTeam" ` + + discoveryConfigYAML = `kind: discovery_config +version: v1 +metadata: + name: my-discovery-config +spec: + discovery_group: mydg1 + aws: + - types: ["ec2"] + regions: ["eu-west-2"] +` ) func TestCreateClusterAuthPreference_WithSupportForSecondFactorWithoutQuotes(t *testing.T) {