diff --git a/api/adhocprofiles/v1/adhocprofiles.proto b/api/adhocprofiles/v1/adhocprofiles.proto new file mode 100644 index 0000000000..5f5591575b --- /dev/null +++ b/api/adhocprofiles/v1/adhocprofiles.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +package adhocprofiles.v1; + +import "types/v1/types.proto"; + +service AdHocProfileService { + // Upload a profile to the underlying store. The request contains a name and a base64 encoded pprof file. The response + // contains a generated unique identifier, a flamegraph and a list of found sample types within the profile. + rpc Upload(AdHocProfilesUploadRequest) returns (AdHocProfilesGetResponse) {} + + // Retrieves a profile from the underlying store by id and an optional sample type. The response is similar to the one + // for the upload method. + rpc Get(AdHocProfilesGetRequest) returns (AdHocProfilesGetResponse) {} + + // Retrieves a list of profiles found in the underlying store. + rpc List(AdHocProfilesListRequest) returns (AdHocProfilesListResponse) {} +} + +message AdHocProfilesUploadRequest { + // This is typically the file name and it serves as a human readable name for the profile. + string name = 1; + // This is the profile encoded in base64. The supported formats are pprof, json, collapsed and perf-script. + string profile = 2; + // Max nodes can be used to truncate the response. + optional int64 max_nodes = 3; +} + +message AdHocProfilesGetRequest { + // The unique identifier of the profile. + string id = 1; + // The desired profile type (e.g., cpu, samples) for the returned flame graph. If omitted the first profile is returned. + optional string profile_type = 2; + // Max nodes can be used to truncate the response. + optional int64 max_nodes = 3; +} + +message AdHocProfilesGetResponse { + string id = 1; + string name = 2; + // timestamp in milliseconds + int64 uploaded_at = 3; + string profile_type = 4; + // Some profiles formats (like pprof) can contain multiple profile (sample) types inside. One of these can be passed + // in the Get request using the profile_type field. + repeated string profile_types = 5; + string flamebearer_profile = 6; +} + +message AdHocProfilesListRequest {} + +message AdHocProfilesListResponse { + repeated AdHocProfilesProfileMetadata profiles = 1; +} + +message AdHocProfilesProfileMetadata { + string id = 1; + string name = 2; + // timestamp in milliseconds + int64 uploaded_at = 3; +} diff --git a/api/gen/proto/go/adhocprofiles/v1/adhocprofiles.pb.go b/api/gen/proto/go/adhocprofiles/v1/adhocprofiles.pb.go new file mode 100644 index 0000000000..35181c8b7c --- /dev/null +++ b/api/gen/proto/go/adhocprofiles/v1/adhocprofiles.pb.go @@ -0,0 +1,620 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: adhocprofiles/v1/adhocprofiles.proto + +package adhocprofilesv1 + +import ( + _ "github.com/grafana/pyroscope/api/gen/proto/go/types/v1" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type AdHocProfilesUploadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // This is typically the file name and it serves as a human readable name for the profile. + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // This is the profile encoded in base64. The supported formats are pprof, json, collapsed and perf-script. + Profile string `protobuf:"bytes,2,opt,name=profile,proto3" json:"profile,omitempty"` + // Max nodes can be used to truncate the response. + MaxNodes *int64 `protobuf:"varint,3,opt,name=max_nodes,json=maxNodes,proto3,oneof" json:"max_nodes,omitempty"` +} + +func (x *AdHocProfilesUploadRequest) Reset() { + *x = AdHocProfilesUploadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdHocProfilesUploadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdHocProfilesUploadRequest) ProtoMessage() {} + +func (x *AdHocProfilesUploadRequest) ProtoReflect() protoreflect.Message { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[0] + 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 AdHocProfilesUploadRequest.ProtoReflect.Descriptor instead. +func (*AdHocProfilesUploadRequest) Descriptor() ([]byte, []int) { + return file_adhocprofiles_v1_adhocprofiles_proto_rawDescGZIP(), []int{0} +} + +func (x *AdHocProfilesUploadRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdHocProfilesUploadRequest) GetProfile() string { + if x != nil { + return x.Profile + } + return "" +} + +func (x *AdHocProfilesUploadRequest) GetMaxNodes() int64 { + if x != nil && x.MaxNodes != nil { + return *x.MaxNodes + } + return 0 +} + +type AdHocProfilesGetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The unique identifier of the profile. + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + // The desired profile type (e.g., cpu, samples) for the returned flame graph. If omitted the first profile is returned. + ProfileType *string `protobuf:"bytes,2,opt,name=profile_type,json=profileType,proto3,oneof" json:"profile_type,omitempty"` + // Max nodes can be used to truncate the response. + MaxNodes *int64 `protobuf:"varint,3,opt,name=max_nodes,json=maxNodes,proto3,oneof" json:"max_nodes,omitempty"` +} + +func (x *AdHocProfilesGetRequest) Reset() { + *x = AdHocProfilesGetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdHocProfilesGetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdHocProfilesGetRequest) ProtoMessage() {} + +func (x *AdHocProfilesGetRequest) ProtoReflect() protoreflect.Message { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[1] + 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 AdHocProfilesGetRequest.ProtoReflect.Descriptor instead. +func (*AdHocProfilesGetRequest) Descriptor() ([]byte, []int) { + return file_adhocprofiles_v1_adhocprofiles_proto_rawDescGZIP(), []int{1} +} + +func (x *AdHocProfilesGetRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdHocProfilesGetRequest) GetProfileType() string { + if x != nil && x.ProfileType != nil { + return *x.ProfileType + } + return "" +} + +func (x *AdHocProfilesGetRequest) GetMaxNodes() int64 { + if x != nil && x.MaxNodes != nil { + return *x.MaxNodes + } + return 0 +} + +type AdHocProfilesGetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // timestamp in milliseconds + UploadedAt int64 `protobuf:"varint,3,opt,name=uploaded_at,json=uploadedAt,proto3" json:"uploaded_at,omitempty"` + ProfileType string `protobuf:"bytes,4,opt,name=profile_type,json=profileType,proto3" json:"profile_type,omitempty"` + // Some profiles formats (like pprof) can contain multiple profile (sample) types inside. One of these can be passed + // in the Get request using the profile_type field. + ProfileTypes []string `protobuf:"bytes,5,rep,name=profile_types,json=profileTypes,proto3" json:"profile_types,omitempty"` + FlamebearerProfile string `protobuf:"bytes,6,opt,name=flamebearer_profile,json=flamebearerProfile,proto3" json:"flamebearer_profile,omitempty"` +} + +func (x *AdHocProfilesGetResponse) Reset() { + *x = AdHocProfilesGetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdHocProfilesGetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdHocProfilesGetResponse) ProtoMessage() {} + +func (x *AdHocProfilesGetResponse) ProtoReflect() protoreflect.Message { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[2] + 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 AdHocProfilesGetResponse.ProtoReflect.Descriptor instead. +func (*AdHocProfilesGetResponse) Descriptor() ([]byte, []int) { + return file_adhocprofiles_v1_adhocprofiles_proto_rawDescGZIP(), []int{2} +} + +func (x *AdHocProfilesGetResponse) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdHocProfilesGetResponse) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdHocProfilesGetResponse) GetUploadedAt() int64 { + if x != nil { + return x.UploadedAt + } + return 0 +} + +func (x *AdHocProfilesGetResponse) GetProfileType() string { + if x != nil { + return x.ProfileType + } + return "" +} + +func (x *AdHocProfilesGetResponse) GetProfileTypes() []string { + if x != nil { + return x.ProfileTypes + } + return nil +} + +func (x *AdHocProfilesGetResponse) GetFlamebearerProfile() string { + if x != nil { + return x.FlamebearerProfile + } + return "" +} + +type AdHocProfilesListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *AdHocProfilesListRequest) Reset() { + *x = AdHocProfilesListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdHocProfilesListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdHocProfilesListRequest) ProtoMessage() {} + +func (x *AdHocProfilesListRequest) ProtoReflect() protoreflect.Message { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[3] + 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 AdHocProfilesListRequest.ProtoReflect.Descriptor instead. +func (*AdHocProfilesListRequest) Descriptor() ([]byte, []int) { + return file_adhocprofiles_v1_adhocprofiles_proto_rawDescGZIP(), []int{3} +} + +type AdHocProfilesListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Profiles []*AdHocProfilesProfileMetadata `protobuf:"bytes,1,rep,name=profiles,proto3" json:"profiles,omitempty"` +} + +func (x *AdHocProfilesListResponse) Reset() { + *x = AdHocProfilesListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdHocProfilesListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdHocProfilesListResponse) ProtoMessage() {} + +func (x *AdHocProfilesListResponse) ProtoReflect() protoreflect.Message { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[4] + 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 AdHocProfilesListResponse.ProtoReflect.Descriptor instead. +func (*AdHocProfilesListResponse) Descriptor() ([]byte, []int) { + return file_adhocprofiles_v1_adhocprofiles_proto_rawDescGZIP(), []int{4} +} + +func (x *AdHocProfilesListResponse) GetProfiles() []*AdHocProfilesProfileMetadata { + if x != nil { + return x.Profiles + } + return nil +} + +type AdHocProfilesProfileMetadata struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // timestamp in milliseconds + UploadedAt int64 `protobuf:"varint,3,opt,name=uploaded_at,json=uploadedAt,proto3" json:"uploaded_at,omitempty"` +} + +func (x *AdHocProfilesProfileMetadata) Reset() { + *x = AdHocProfilesProfileMetadata{} + if protoimpl.UnsafeEnabled { + mi := &file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AdHocProfilesProfileMetadata) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AdHocProfilesProfileMetadata) ProtoMessage() {} + +func (x *AdHocProfilesProfileMetadata) ProtoReflect() protoreflect.Message { + mi := &file_adhocprofiles_v1_adhocprofiles_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 AdHocProfilesProfileMetadata.ProtoReflect.Descriptor instead. +func (*AdHocProfilesProfileMetadata) Descriptor() ([]byte, []int) { + return file_adhocprofiles_v1_adhocprofiles_proto_rawDescGZIP(), []int{5} +} + +func (x *AdHocProfilesProfileMetadata) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *AdHocProfilesProfileMetadata) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *AdHocProfilesProfileMetadata) GetUploadedAt() int64 { + if x != nil { + return x.UploadedAt + } + return 0 +} + +var File_adhocprofiles_v1_adhocprofiles_proto protoreflect.FileDescriptor + +var file_adhocprofiles_v1_adhocprofiles_proto_rawDesc = []byte{ + 0x0a, 0x24, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2f, + 0x76, 0x31, 0x2f, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x10, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x1a, 0x14, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7a, + 0x0a, 0x1a, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x55, + 0x70, 0x6c, 0x6f, 0x61, 0x64, 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, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x09, 0x6d, 0x61, + 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, + 0x08, 0x6d, 0x61, 0x78, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, 0x92, 0x01, 0x0a, 0x17, 0x41, + 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x26, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, + 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x88, 0x01, 0x01, 0x12, 0x20, + 0x0a, 0x09, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x03, 0x48, 0x01, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x88, 0x01, 0x01, + 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6d, 0x61, 0x78, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x73, 0x22, + 0xd8, 0x01, 0x0a, 0x18, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x41, + 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x2f, 0x0a, 0x13, 0x66, 0x6c, 0x61, + 0x6d, 0x65, 0x62, 0x65, 0x61, 0x72, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, 0x66, 0x6c, 0x61, 0x6d, 0x65, 0x62, 0x65, 0x61, + 0x72, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x64, + 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x67, 0x0a, 0x19, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x4a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, + 0x63, 0x0a, 0x1c, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x5f, + 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, + 0x65, 0x64, 0x41, 0x74, 0x32, 0xbe, 0x02, 0x0a, 0x13, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x64, 0x0a, 0x06, + 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x2c, 0x2e, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x29, 0x2e, 0x61, 0x64, 0x68, 0x6f, + 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x48, + 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x12, 0x61, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2a, 0x2e, 0x61, 0x64, 0x68, + 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, + 0x48, 0x6f, 0x63, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2b, 0x2e, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x64, 0x48, 0x6f, 0x63, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xdb, 0x01, 0x0a, 0x14, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x64, + 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x76, 0x31, 0x42, 0x12, + 0x41, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x67, 0x72, 0x61, 0x66, 0x61, 0x6e, 0x61, 0x2f, 0x70, 0x79, 0x72, 0x6f, 0x73, 0x63, 0x6f, + 0x70, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x6f, 0x2f, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x2f, 0x76, 0x31, 0x3b, 0x61, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x76, 0x31, 0xa2, 0x02, 0x03, 0x41, 0x58, 0x58, 0xaa, 0x02, 0x10, 0x41, 0x64, 0x68, + 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x2e, 0x56, 0x31, 0xca, 0x02, 0x10, + 0x41, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x5c, 0x56, 0x31, + 0xe2, 0x02, 0x1c, 0x41, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x5c, 0x56, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, + 0x02, 0x11, 0x41, 0x64, 0x68, 0x6f, 0x63, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x3a, + 0x3a, 0x56, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_adhocprofiles_v1_adhocprofiles_proto_rawDescOnce sync.Once + file_adhocprofiles_v1_adhocprofiles_proto_rawDescData = file_adhocprofiles_v1_adhocprofiles_proto_rawDesc +) + +func file_adhocprofiles_v1_adhocprofiles_proto_rawDescGZIP() []byte { + file_adhocprofiles_v1_adhocprofiles_proto_rawDescOnce.Do(func() { + file_adhocprofiles_v1_adhocprofiles_proto_rawDescData = protoimpl.X.CompressGZIP(file_adhocprofiles_v1_adhocprofiles_proto_rawDescData) + }) + return file_adhocprofiles_v1_adhocprofiles_proto_rawDescData +} + +var file_adhocprofiles_v1_adhocprofiles_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_adhocprofiles_v1_adhocprofiles_proto_goTypes = []interface{}{ + (*AdHocProfilesUploadRequest)(nil), // 0: adhocprofiles.v1.AdHocProfilesUploadRequest + (*AdHocProfilesGetRequest)(nil), // 1: adhocprofiles.v1.AdHocProfilesGetRequest + (*AdHocProfilesGetResponse)(nil), // 2: adhocprofiles.v1.AdHocProfilesGetResponse + (*AdHocProfilesListRequest)(nil), // 3: adhocprofiles.v1.AdHocProfilesListRequest + (*AdHocProfilesListResponse)(nil), // 4: adhocprofiles.v1.AdHocProfilesListResponse + (*AdHocProfilesProfileMetadata)(nil), // 5: adhocprofiles.v1.AdHocProfilesProfileMetadata +} +var file_adhocprofiles_v1_adhocprofiles_proto_depIdxs = []int32{ + 5, // 0: adhocprofiles.v1.AdHocProfilesListResponse.profiles:type_name -> adhocprofiles.v1.AdHocProfilesProfileMetadata + 0, // 1: adhocprofiles.v1.AdHocProfileService.Upload:input_type -> adhocprofiles.v1.AdHocProfilesUploadRequest + 1, // 2: adhocprofiles.v1.AdHocProfileService.Get:input_type -> adhocprofiles.v1.AdHocProfilesGetRequest + 3, // 3: adhocprofiles.v1.AdHocProfileService.List:input_type -> adhocprofiles.v1.AdHocProfilesListRequest + 2, // 4: adhocprofiles.v1.AdHocProfileService.Upload:output_type -> adhocprofiles.v1.AdHocProfilesGetResponse + 2, // 5: adhocprofiles.v1.AdHocProfileService.Get:output_type -> adhocprofiles.v1.AdHocProfilesGetResponse + 4, // 6: adhocprofiles.v1.AdHocProfileService.List:output_type -> adhocprofiles.v1.AdHocProfilesListResponse + 4, // [4:7] is the sub-list for method output_type + 1, // [1:4] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_adhocprofiles_v1_adhocprofiles_proto_init() } +func file_adhocprofiles_v1_adhocprofiles_proto_init() { + if File_adhocprofiles_v1_adhocprofiles_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdHocProfilesUploadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdHocProfilesGetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdHocProfilesGetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdHocProfilesListRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdHocProfilesListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AdHocProfilesProfileMetadata); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_adhocprofiles_v1_adhocprofiles_proto_msgTypes[1].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_adhocprofiles_v1_adhocprofiles_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_adhocprofiles_v1_adhocprofiles_proto_goTypes, + DependencyIndexes: file_adhocprofiles_v1_adhocprofiles_proto_depIdxs, + MessageInfos: file_adhocprofiles_v1_adhocprofiles_proto_msgTypes, + }.Build() + File_adhocprofiles_v1_adhocprofiles_proto = out.File + file_adhocprofiles_v1_adhocprofiles_proto_rawDesc = nil + file_adhocprofiles_v1_adhocprofiles_proto_goTypes = nil + file_adhocprofiles_v1_adhocprofiles_proto_depIdxs = nil +} diff --git a/api/gen/proto/go/adhocprofiles/v1/adhocprofiles_vtproto.pb.go b/api/gen/proto/go/adhocprofiles/v1/adhocprofiles_vtproto.pb.go new file mode 100644 index 0000000000..8dad7cf3ae --- /dev/null +++ b/api/gen/proto/go/adhocprofiles/v1/adhocprofiles_vtproto.pb.go @@ -0,0 +1,1804 @@ +// Code generated by protoc-gen-go-vtproto. DO NOT EDIT. +// protoc-gen-go-vtproto version: v0.0.0-20230725111439-5b3aae6571b8 +// source: adhocprofiles/v1/adhocprofiles.proto + +package adhocprofilesv1 + +import ( + context "context" + fmt "fmt" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + proto "google.golang.org/protobuf/proto" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + io "io" + bits "math/bits" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +func (m *AdHocProfilesUploadRequest) CloneVT() *AdHocProfilesUploadRequest { + if m == nil { + return (*AdHocProfilesUploadRequest)(nil) + } + r := &AdHocProfilesUploadRequest{ + Name: m.Name, + Profile: m.Profile, + } + if rhs := m.MaxNodes; rhs != nil { + tmpVal := *rhs + r.MaxNodes = &tmpVal + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *AdHocProfilesUploadRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *AdHocProfilesGetRequest) CloneVT() *AdHocProfilesGetRequest { + if m == nil { + return (*AdHocProfilesGetRequest)(nil) + } + r := &AdHocProfilesGetRequest{ + Id: m.Id, + } + if rhs := m.ProfileType; rhs != nil { + tmpVal := *rhs + r.ProfileType = &tmpVal + } + if rhs := m.MaxNodes; rhs != nil { + tmpVal := *rhs + r.MaxNodes = &tmpVal + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *AdHocProfilesGetRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *AdHocProfilesGetResponse) CloneVT() *AdHocProfilesGetResponse { + if m == nil { + return (*AdHocProfilesGetResponse)(nil) + } + r := &AdHocProfilesGetResponse{ + Id: m.Id, + Name: m.Name, + UploadedAt: m.UploadedAt, + ProfileType: m.ProfileType, + FlamebearerProfile: m.FlamebearerProfile, + } + if rhs := m.ProfileTypes; rhs != nil { + tmpContainer := make([]string, len(rhs)) + copy(tmpContainer, rhs) + r.ProfileTypes = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *AdHocProfilesGetResponse) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *AdHocProfilesListRequest) CloneVT() *AdHocProfilesListRequest { + if m == nil { + return (*AdHocProfilesListRequest)(nil) + } + r := &AdHocProfilesListRequest{} + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *AdHocProfilesListRequest) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *AdHocProfilesListResponse) CloneVT() *AdHocProfilesListResponse { + if m == nil { + return (*AdHocProfilesListResponse)(nil) + } + r := &AdHocProfilesListResponse{} + if rhs := m.Profiles; rhs != nil { + tmpContainer := make([]*AdHocProfilesProfileMetadata, len(rhs)) + for k, v := range rhs { + tmpContainer[k] = v.CloneVT() + } + r.Profiles = tmpContainer + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *AdHocProfilesListResponse) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (m *AdHocProfilesProfileMetadata) CloneVT() *AdHocProfilesProfileMetadata { + if m == nil { + return (*AdHocProfilesProfileMetadata)(nil) + } + r := &AdHocProfilesProfileMetadata{ + Id: m.Id, + Name: m.Name, + UploadedAt: m.UploadedAt, + } + if len(m.unknownFields) > 0 { + r.unknownFields = make([]byte, len(m.unknownFields)) + copy(r.unknownFields, m.unknownFields) + } + return r +} + +func (m *AdHocProfilesProfileMetadata) CloneMessageVT() proto.Message { + return m.CloneVT() +} + +func (this *AdHocProfilesUploadRequest) EqualVT(that *AdHocProfilesUploadRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Name != that.Name { + return false + } + if this.Profile != that.Profile { + return false + } + if p, q := this.MaxNodes, that.MaxNodes; (p == nil && q != nil) || (p != nil && (q == nil || *p != *q)) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *AdHocProfilesUploadRequest) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*AdHocProfilesUploadRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *AdHocProfilesGetRequest) EqualVT(that *AdHocProfilesGetRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Id != that.Id { + return false + } + if p, q := this.ProfileType, that.ProfileType; (p == nil && q != nil) || (p != nil && (q == nil || *p != *q)) { + return false + } + if p, q := this.MaxNodes, that.MaxNodes; (p == nil && q != nil) || (p != nil && (q == nil || *p != *q)) { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *AdHocProfilesGetRequest) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*AdHocProfilesGetRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *AdHocProfilesGetResponse) EqualVT(that *AdHocProfilesGetResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Id != that.Id { + return false + } + if this.Name != that.Name { + return false + } + if this.UploadedAt != that.UploadedAt { + return false + } + if this.ProfileType != that.ProfileType { + return false + } + if len(this.ProfileTypes) != len(that.ProfileTypes) { + return false + } + for i, vx := range this.ProfileTypes { + vy := that.ProfileTypes[i] + if vx != vy { + return false + } + } + if this.FlamebearerProfile != that.FlamebearerProfile { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *AdHocProfilesGetResponse) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*AdHocProfilesGetResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *AdHocProfilesListRequest) EqualVT(that *AdHocProfilesListRequest) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *AdHocProfilesListRequest) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*AdHocProfilesListRequest) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *AdHocProfilesListResponse) EqualVT(that *AdHocProfilesListResponse) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if len(this.Profiles) != len(that.Profiles) { + return false + } + for i, vx := range this.Profiles { + vy := that.Profiles[i] + if p, q := vx, vy; p != q { + if p == nil { + p = &AdHocProfilesProfileMetadata{} + } + if q == nil { + q = &AdHocProfilesProfileMetadata{} + } + if !p.EqualVT(q) { + return false + } + } + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *AdHocProfilesListResponse) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*AdHocProfilesListResponse) + if !ok { + return false + } + return this.EqualVT(that) +} +func (this *AdHocProfilesProfileMetadata) EqualVT(that *AdHocProfilesProfileMetadata) bool { + if this == that { + return true + } else if this == nil || that == nil { + return false + } + if this.Id != that.Id { + return false + } + if this.Name != that.Name { + return false + } + if this.UploadedAt != that.UploadedAt { + return false + } + return string(this.unknownFields) == string(that.unknownFields) +} + +func (this *AdHocProfilesProfileMetadata) EqualMessageVT(thatMsg proto.Message) bool { + that, ok := thatMsg.(*AdHocProfilesProfileMetadata) + if !ok { + return false + } + return this.EqualVT(that) +} + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.32.0 or later. +const _ = grpc.SupportPackageIsVersion7 + +// AdHocProfileServiceClient is the client API for AdHocProfileService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type AdHocProfileServiceClient interface { + // Upload a profile to the underlying store. The request contains a name and a base64 encoded pprof file. The response + // contains a generated unique identifier, a flamegraph and a list of found sample types within the profile. + Upload(ctx context.Context, in *AdHocProfilesUploadRequest, opts ...grpc.CallOption) (*AdHocProfilesGetResponse, error) + // Retrieves a profile from the underlying store by id and an optional sample type. The response is similar to the one + // for the upload method. + Get(ctx context.Context, in *AdHocProfilesGetRequest, opts ...grpc.CallOption) (*AdHocProfilesGetResponse, error) + // Retrieves a list of profiles found in the underlying store. + List(ctx context.Context, in *AdHocProfilesListRequest, opts ...grpc.CallOption) (*AdHocProfilesListResponse, error) +} + +type adHocProfileServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewAdHocProfileServiceClient(cc grpc.ClientConnInterface) AdHocProfileServiceClient { + return &adHocProfileServiceClient{cc} +} + +func (c *adHocProfileServiceClient) Upload(ctx context.Context, in *AdHocProfilesUploadRequest, opts ...grpc.CallOption) (*AdHocProfilesGetResponse, error) { + out := new(AdHocProfilesGetResponse) + err := c.cc.Invoke(ctx, "/adhocprofiles.v1.AdHocProfileService/Upload", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adHocProfileServiceClient) Get(ctx context.Context, in *AdHocProfilesGetRequest, opts ...grpc.CallOption) (*AdHocProfilesGetResponse, error) { + out := new(AdHocProfilesGetResponse) + err := c.cc.Invoke(ctx, "/adhocprofiles.v1.AdHocProfileService/Get", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *adHocProfileServiceClient) List(ctx context.Context, in *AdHocProfilesListRequest, opts ...grpc.CallOption) (*AdHocProfilesListResponse, error) { + out := new(AdHocProfilesListResponse) + err := c.cc.Invoke(ctx, "/adhocprofiles.v1.AdHocProfileService/List", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// AdHocProfileServiceServer is the server API for AdHocProfileService service. +// All implementations must embed UnimplementedAdHocProfileServiceServer +// for forward compatibility +type AdHocProfileServiceServer interface { + // Upload a profile to the underlying store. The request contains a name and a base64 encoded pprof file. The response + // contains a generated unique identifier, a flamegraph and a list of found sample types within the profile. + Upload(context.Context, *AdHocProfilesUploadRequest) (*AdHocProfilesGetResponse, error) + // Retrieves a profile from the underlying store by id and an optional sample type. The response is similar to the one + // for the upload method. + Get(context.Context, *AdHocProfilesGetRequest) (*AdHocProfilesGetResponse, error) + // Retrieves a list of profiles found in the underlying store. + List(context.Context, *AdHocProfilesListRequest) (*AdHocProfilesListResponse, error) + mustEmbedUnimplementedAdHocProfileServiceServer() +} + +// UnimplementedAdHocProfileServiceServer must be embedded to have forward compatible implementations. +type UnimplementedAdHocProfileServiceServer struct { +} + +func (UnimplementedAdHocProfileServiceServer) Upload(context.Context, *AdHocProfilesUploadRequest) (*AdHocProfilesGetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Upload not implemented") +} +func (UnimplementedAdHocProfileServiceServer) Get(context.Context, *AdHocProfilesGetRequest) (*AdHocProfilesGetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Get not implemented") +} +func (UnimplementedAdHocProfileServiceServer) List(context.Context, *AdHocProfilesListRequest) (*AdHocProfilesListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (UnimplementedAdHocProfileServiceServer) mustEmbedUnimplementedAdHocProfileServiceServer() {} + +// UnsafeAdHocProfileServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to AdHocProfileServiceServer will +// result in compilation errors. +type UnsafeAdHocProfileServiceServer interface { + mustEmbedUnimplementedAdHocProfileServiceServer() +} + +func RegisterAdHocProfileServiceServer(s grpc.ServiceRegistrar, srv AdHocProfileServiceServer) { + s.RegisterService(&AdHocProfileService_ServiceDesc, srv) +} + +func _AdHocProfileService_Upload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AdHocProfilesUploadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdHocProfileServiceServer).Upload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/adhocprofiles.v1.AdHocProfileService/Upload", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdHocProfileServiceServer).Upload(ctx, req.(*AdHocProfilesUploadRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdHocProfileService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AdHocProfilesGetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdHocProfileServiceServer).Get(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/adhocprofiles.v1.AdHocProfileService/Get", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdHocProfileServiceServer).Get(ctx, req.(*AdHocProfilesGetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _AdHocProfileService_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AdHocProfilesListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AdHocProfileServiceServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/adhocprofiles.v1.AdHocProfileService/List", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AdHocProfileServiceServer).List(ctx, req.(*AdHocProfilesListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// AdHocProfileService_ServiceDesc is the grpc.ServiceDesc for AdHocProfileService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var AdHocProfileService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "adhocprofiles.v1.AdHocProfileService", + HandlerType: (*AdHocProfileServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Upload", + Handler: _AdHocProfileService_Upload_Handler, + }, + { + MethodName: "Get", + Handler: _AdHocProfileService_Get_Handler, + }, + { + MethodName: "List", + Handler: _AdHocProfileService_List_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "adhocprofiles/v1/adhocprofiles.proto", +} + +func (m *AdHocProfilesUploadRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AdHocProfilesUploadRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *AdHocProfilesUploadRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.MaxNodes != nil { + i = encodeVarint(dAtA, i, uint64(*m.MaxNodes)) + i-- + dAtA[i] = 0x18 + } + if len(m.Profile) > 0 { + i -= len(m.Profile) + copy(dAtA[i:], m.Profile) + i = encodeVarint(dAtA, i, uint64(len(m.Profile))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AdHocProfilesGetRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AdHocProfilesGetRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *AdHocProfilesGetRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.MaxNodes != nil { + i = encodeVarint(dAtA, i, uint64(*m.MaxNodes)) + i-- + dAtA[i] = 0x18 + } + if m.ProfileType != nil { + i -= len(*m.ProfileType) + copy(dAtA[i:], *m.ProfileType) + i = encodeVarint(dAtA, i, uint64(len(*m.ProfileType))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarint(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AdHocProfilesGetResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AdHocProfilesGetResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *AdHocProfilesGetResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.FlamebearerProfile) > 0 { + i -= len(m.FlamebearerProfile) + copy(dAtA[i:], m.FlamebearerProfile) + i = encodeVarint(dAtA, i, uint64(len(m.FlamebearerProfile))) + i-- + dAtA[i] = 0x32 + } + if len(m.ProfileTypes) > 0 { + for iNdEx := len(m.ProfileTypes) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.ProfileTypes[iNdEx]) + copy(dAtA[i:], m.ProfileTypes[iNdEx]) + i = encodeVarint(dAtA, i, uint64(len(m.ProfileTypes[iNdEx]))) + i-- + dAtA[i] = 0x2a + } + } + if len(m.ProfileType) > 0 { + i -= len(m.ProfileType) + copy(dAtA[i:], m.ProfileType) + i = encodeVarint(dAtA, i, uint64(len(m.ProfileType))) + i-- + dAtA[i] = 0x22 + } + if m.UploadedAt != 0 { + i = encodeVarint(dAtA, i, uint64(m.UploadedAt)) + i-- + dAtA[i] = 0x18 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarint(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *AdHocProfilesListRequest) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AdHocProfilesListRequest) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *AdHocProfilesListRequest) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + return len(dAtA) - i, nil +} + +func (m *AdHocProfilesListResponse) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AdHocProfilesListResponse) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *AdHocProfilesListResponse) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if len(m.Profiles) > 0 { + for iNdEx := len(m.Profiles) - 1; iNdEx >= 0; iNdEx-- { + size, err := m.Profiles[iNdEx].MarshalToSizedBufferVT(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarint(dAtA, i, uint64(size)) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *AdHocProfilesProfileMetadata) MarshalVT() (dAtA []byte, err error) { + if m == nil { + return nil, nil + } + size := m.SizeVT() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBufferVT(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AdHocProfilesProfileMetadata) MarshalToVT(dAtA []byte) (int, error) { + size := m.SizeVT() + return m.MarshalToSizedBufferVT(dAtA[:size]) +} + +func (m *AdHocProfilesProfileMetadata) MarshalToSizedBufferVT(dAtA []byte) (int, error) { + if m == nil { + return 0, nil + } + i := len(dAtA) + _ = i + var l int + _ = l + if m.unknownFields != nil { + i -= len(m.unknownFields) + copy(dAtA[i:], m.unknownFields) + } + if m.UploadedAt != 0 { + i = encodeVarint(dAtA, i, uint64(m.UploadedAt)) + i-- + dAtA[i] = 0x18 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarint(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarint(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarint(dAtA []byte, offset int, v uint64) int { + offset -= sov(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *AdHocProfilesUploadRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + l = len(m.Profile) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.MaxNodes != nil { + n += 1 + sov(uint64(*m.MaxNodes)) + } + n += len(m.unknownFields) + return n +} + +func (m *AdHocProfilesGetRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.ProfileType != nil { + l = len(*m.ProfileType) + n += 1 + l + sov(uint64(l)) + } + if m.MaxNodes != nil { + n += 1 + sov(uint64(*m.MaxNodes)) + } + n += len(m.unknownFields) + return n +} + +func (m *AdHocProfilesGetResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.UploadedAt != 0 { + n += 1 + sov(uint64(m.UploadedAt)) + } + l = len(m.ProfileType) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if len(m.ProfileTypes) > 0 { + for _, s := range m.ProfileTypes { + l = len(s) + n += 1 + l + sov(uint64(l)) + } + } + l = len(m.FlamebearerProfile) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + n += len(m.unknownFields) + return n +} + +func (m *AdHocProfilesListRequest) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + n += len(m.unknownFields) + return n +} + +func (m *AdHocProfilesListResponse) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Profiles) > 0 { + for _, e := range m.Profiles { + l = e.SizeVT() + n += 1 + l + sov(uint64(l)) + } + } + n += len(m.unknownFields) + return n +} + +func (m *AdHocProfilesProfileMetadata) SizeVT() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sov(uint64(l)) + } + if m.UploadedAt != 0 { + n += 1 + sov(uint64(m.UploadedAt)) + } + n += len(m.unknownFields) + return n +} + +func sov(x uint64) (n int) { + return (bits.Len64(x|1) + 6) / 7 +} +func soz(x uint64) (n int) { + return sov(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *AdHocProfilesUploadRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AdHocProfilesUploadRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AdHocProfilesUploadRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Profile", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Profile = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxNodes", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.MaxNodes = &v + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AdHocProfilesGetRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AdHocProfilesGetRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AdHocProfilesGetRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProfileType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + s := string(dAtA[iNdEx:postIndex]) + m.ProfileType = &s + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxNodes", wireType) + } + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.MaxNodes = &v + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AdHocProfilesGetResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AdHocProfilesGetResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AdHocProfilesGetResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UploadedAt", wireType) + } + m.UploadedAt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UploadedAt |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProfileType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProfileType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProfileTypes", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ProfileTypes = append(m.ProfileTypes, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FlamebearerProfile", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FlamebearerProfile = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AdHocProfilesListRequest) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AdHocProfilesListRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AdHocProfilesListRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AdHocProfilesListResponse) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AdHocProfilesListResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AdHocProfilesListResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Profiles", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Profiles = append(m.Profiles, &AdHocProfilesProfileMetadata{}) + if err := m.Profiles[len(m.Profiles)-1].UnmarshalVT(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AdHocProfilesProfileMetadata) UnmarshalVT(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AdHocProfilesProfileMetadata: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AdHocProfilesProfileMetadata: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field UploadedAt", wireType) + } + m.UploadedAt = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.UploadedAt |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skip(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLength + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.unknownFields = append(m.unknownFields, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} + +func skip(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflow + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLength + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroup + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLength + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLength = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflow = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroup = fmt.Errorf("proto: unexpected end of group") +) diff --git a/api/gen/proto/go/adhocprofiles/v1/adhocprofilesv1connect/adhocprofiles.connect.go b/api/gen/proto/go/adhocprofiles/v1/adhocprofilesv1connect/adhocprofiles.connect.go new file mode 100644 index 0000000000..b93f7ff74e --- /dev/null +++ b/api/gen/proto/go/adhocprofiles/v1/adhocprofilesv1connect/adhocprofiles.connect.go @@ -0,0 +1,183 @@ +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: adhocprofiles/v1/adhocprofiles.proto + +package adhocprofilesv1connect + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + v1 "github.com/grafana/pyroscope/api/gen/proto/go/adhocprofiles/v1" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // AdHocProfileServiceName is the fully-qualified name of the AdHocProfileService service. + AdHocProfileServiceName = "adhocprofiles.v1.AdHocProfileService" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // AdHocProfileServiceUploadProcedure is the fully-qualified name of the AdHocProfileService's + // Upload RPC. + AdHocProfileServiceUploadProcedure = "/adhocprofiles.v1.AdHocProfileService/Upload" + // AdHocProfileServiceGetProcedure is the fully-qualified name of the AdHocProfileService's Get RPC. + AdHocProfileServiceGetProcedure = "/adhocprofiles.v1.AdHocProfileService/Get" + // AdHocProfileServiceListProcedure is the fully-qualified name of the AdHocProfileService's List + // RPC. + AdHocProfileServiceListProcedure = "/adhocprofiles.v1.AdHocProfileService/List" +) + +// These variables are the protoreflect.Descriptor objects for the RPCs defined in this package. +var ( + adHocProfileServiceServiceDescriptor = v1.File_adhocprofiles_v1_adhocprofiles_proto.Services().ByName("AdHocProfileService") + adHocProfileServiceUploadMethodDescriptor = adHocProfileServiceServiceDescriptor.Methods().ByName("Upload") + adHocProfileServiceGetMethodDescriptor = adHocProfileServiceServiceDescriptor.Methods().ByName("Get") + adHocProfileServiceListMethodDescriptor = adHocProfileServiceServiceDescriptor.Methods().ByName("List") +) + +// AdHocProfileServiceClient is a client for the adhocprofiles.v1.AdHocProfileService service. +type AdHocProfileServiceClient interface { + // Upload a profile to the underlying store. The request contains a name and a base64 encoded pprof file. The response + // contains a generated unique identifier, a flamegraph and a list of found sample types within the profile. + Upload(context.Context, *connect.Request[v1.AdHocProfilesUploadRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) + // Retrieves a profile from the underlying store by id and an optional sample type. The response is similar to the one + // for the upload method. + Get(context.Context, *connect.Request[v1.AdHocProfilesGetRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) + // Retrieves a list of profiles found in the underlying store. + List(context.Context, *connect.Request[v1.AdHocProfilesListRequest]) (*connect.Response[v1.AdHocProfilesListResponse], error) +} + +// NewAdHocProfileServiceClient constructs a client for the adhocprofiles.v1.AdHocProfileService +// service. By default, it uses the Connect protocol with the binary Protobuf Codec, asks for +// gzipped responses, and sends uncompressed requests. To use the gRPC or gRPC-Web protocols, supply +// the connect.WithGRPC() or connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewAdHocProfileServiceClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) AdHocProfileServiceClient { + baseURL = strings.TrimRight(baseURL, "/") + return &adHocProfileServiceClient{ + upload: connect.NewClient[v1.AdHocProfilesUploadRequest, v1.AdHocProfilesGetResponse]( + httpClient, + baseURL+AdHocProfileServiceUploadProcedure, + connect.WithSchema(adHocProfileServiceUploadMethodDescriptor), + connect.WithClientOptions(opts...), + ), + get: connect.NewClient[v1.AdHocProfilesGetRequest, v1.AdHocProfilesGetResponse]( + httpClient, + baseURL+AdHocProfileServiceGetProcedure, + connect.WithSchema(adHocProfileServiceGetMethodDescriptor), + connect.WithClientOptions(opts...), + ), + list: connect.NewClient[v1.AdHocProfilesListRequest, v1.AdHocProfilesListResponse]( + httpClient, + baseURL+AdHocProfileServiceListProcedure, + connect.WithSchema(adHocProfileServiceListMethodDescriptor), + connect.WithClientOptions(opts...), + ), + } +} + +// adHocProfileServiceClient implements AdHocProfileServiceClient. +type adHocProfileServiceClient struct { + upload *connect.Client[v1.AdHocProfilesUploadRequest, v1.AdHocProfilesGetResponse] + get *connect.Client[v1.AdHocProfilesGetRequest, v1.AdHocProfilesGetResponse] + list *connect.Client[v1.AdHocProfilesListRequest, v1.AdHocProfilesListResponse] +} + +// Upload calls adhocprofiles.v1.AdHocProfileService.Upload. +func (c *adHocProfileServiceClient) Upload(ctx context.Context, req *connect.Request[v1.AdHocProfilesUploadRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) { + return c.upload.CallUnary(ctx, req) +} + +// Get calls adhocprofiles.v1.AdHocProfileService.Get. +func (c *adHocProfileServiceClient) Get(ctx context.Context, req *connect.Request[v1.AdHocProfilesGetRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) { + return c.get.CallUnary(ctx, req) +} + +// List calls adhocprofiles.v1.AdHocProfileService.List. +func (c *adHocProfileServiceClient) List(ctx context.Context, req *connect.Request[v1.AdHocProfilesListRequest]) (*connect.Response[v1.AdHocProfilesListResponse], error) { + return c.list.CallUnary(ctx, req) +} + +// AdHocProfileServiceHandler is an implementation of the adhocprofiles.v1.AdHocProfileService +// service. +type AdHocProfileServiceHandler interface { + // Upload a profile to the underlying store. The request contains a name and a base64 encoded pprof file. The response + // contains a generated unique identifier, a flamegraph and a list of found sample types within the profile. + Upload(context.Context, *connect.Request[v1.AdHocProfilesUploadRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) + // Retrieves a profile from the underlying store by id and an optional sample type. The response is similar to the one + // for the upload method. + Get(context.Context, *connect.Request[v1.AdHocProfilesGetRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) + // Retrieves a list of profiles found in the underlying store. + List(context.Context, *connect.Request[v1.AdHocProfilesListRequest]) (*connect.Response[v1.AdHocProfilesListResponse], error) +} + +// NewAdHocProfileServiceHandler builds an HTTP handler from the service implementation. It returns +// the path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewAdHocProfileServiceHandler(svc AdHocProfileServiceHandler, opts ...connect.HandlerOption) (string, http.Handler) { + adHocProfileServiceUploadHandler := connect.NewUnaryHandler( + AdHocProfileServiceUploadProcedure, + svc.Upload, + connect.WithSchema(adHocProfileServiceUploadMethodDescriptor), + connect.WithHandlerOptions(opts...), + ) + adHocProfileServiceGetHandler := connect.NewUnaryHandler( + AdHocProfileServiceGetProcedure, + svc.Get, + connect.WithSchema(adHocProfileServiceGetMethodDescriptor), + connect.WithHandlerOptions(opts...), + ) + adHocProfileServiceListHandler := connect.NewUnaryHandler( + AdHocProfileServiceListProcedure, + svc.List, + connect.WithSchema(adHocProfileServiceListMethodDescriptor), + connect.WithHandlerOptions(opts...), + ) + return "/adhocprofiles.v1.AdHocProfileService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case AdHocProfileServiceUploadProcedure: + adHocProfileServiceUploadHandler.ServeHTTP(w, r) + case AdHocProfileServiceGetProcedure: + adHocProfileServiceGetHandler.ServeHTTP(w, r) + case AdHocProfileServiceListProcedure: + adHocProfileServiceListHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedAdHocProfileServiceHandler returns CodeUnimplemented from all methods. +type UnimplementedAdHocProfileServiceHandler struct{} + +func (UnimplementedAdHocProfileServiceHandler) Upload(context.Context, *connect.Request[v1.AdHocProfilesUploadRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("adhocprofiles.v1.AdHocProfileService.Upload is not implemented")) +} + +func (UnimplementedAdHocProfileServiceHandler) Get(context.Context, *connect.Request[v1.AdHocProfilesGetRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("adhocprofiles.v1.AdHocProfileService.Get is not implemented")) +} + +func (UnimplementedAdHocProfileServiceHandler) List(context.Context, *connect.Request[v1.AdHocProfilesListRequest]) (*connect.Response[v1.AdHocProfilesListResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("adhocprofiles.v1.AdHocProfileService.List is not implemented")) +} diff --git a/api/gen/proto/go/adhocprofiles/v1/adhocprofilesv1connect/adhocprofiles.connect.mux.go b/api/gen/proto/go/adhocprofiles/v1/adhocprofilesv1connect/adhocprofiles.connect.mux.go new file mode 100644 index 0000000000..6984eb7543 --- /dev/null +++ b/api/gen/proto/go/adhocprofiles/v1/adhocprofilesv1connect/adhocprofiles.connect.mux.go @@ -0,0 +1,37 @@ +// Code generated by protoc-gen-connect-go-mux. DO NOT EDIT. +// +// Source: adhocprofiles/v1/adhocprofiles.proto + +package adhocprofilesv1connect + +import ( + connect "connectrpc.com/connect" + mux "github.com/gorilla/mux" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion0_1_0 + +// RegisterAdHocProfileServiceHandler register an HTTP handler to a mux.Router from the service +// implementation. +func RegisterAdHocProfileServiceHandler(mux *mux.Router, svc AdHocProfileServiceHandler, opts ...connect.HandlerOption) { + mux.Handle("/adhocprofiles.v1.AdHocProfileService/Upload", connect.NewUnaryHandler( + "/adhocprofiles.v1.AdHocProfileService/Upload", + svc.Upload, + opts..., + )) + mux.Handle("/adhocprofiles.v1.AdHocProfileService/Get", connect.NewUnaryHandler( + "/adhocprofiles.v1.AdHocProfileService/Get", + svc.Get, + opts..., + )) + mux.Handle("/adhocprofiles.v1.AdHocProfileService/List", connect.NewUnaryHandler( + "/adhocprofiles.v1.AdHocProfileService/List", + svc.List, + opts..., + )) +} diff --git a/api/openapiv2/gen/phlare.swagger.json b/api/openapiv2/gen/phlare.swagger.json index c54d3df11e..722d777026 100644 --- a/api/openapiv2/gen/phlare.swagger.json +++ b/api/openapiv2/gen/phlare.swagger.json @@ -1,10 +1,13 @@ { "swagger": "2.0", "info": { - "title": "google/v1/profile.proto", + "title": "types/v1/types.proto", "version": "version not set" }, "tags": [ + { + "name": "AdHocProfileService" + }, { "name": "PusherService" }, @@ -388,6 +391,63 @@ } } }, + "v1AdHocProfilesGetResponse": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uploadedAt": { + "type": "string", + "format": "int64", + "title": "timestamp in milliseconds" + }, + "profileType": { + "type": "string" + }, + "profileTypes": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Some profiles formats (like pprof) can contain multiple profile (sample) types inside. One of these can be passed\nin the Get request using the profile_type field." + }, + "flamebearerProfile": { + "type": "string" + } + } + }, + "v1AdHocProfilesListResponse": { + "type": "object", + "properties": { + "profiles": { + "type": "array", + "items": { + "type": "object", + "$ref": "#/definitions/v1AdHocProfilesProfileMetadata" + } + } + } + }, + "v1AdHocProfilesProfileMetadata": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "uploadedAt": { + "type": "string", + "format": "int64", + "title": "timestamp in milliseconds" + } + } + }, "v1BlockCompaction": { "type": "object", "properties": { diff --git a/pkg/adhocprofiles/adhocprofiles.go b/pkg/adhocprofiles/adhocprofiles.go new file mode 100644 index 0000000000..e8563a7f89 --- /dev/null +++ b/pkg/adhocprofiles/adhocprofiles.go @@ -0,0 +1,249 @@ +package adhocprofiles + +import ( + "bytes" + "context" + "crypto/rand" + "encoding/base64" + "encoding/json" + "io" + "strings" + "time" + + "connectrpc.com/connect" + "github.com/go-kit/log" + "github.com/go-kit/log/level" + "github.com/grafana/dskit/services" + "github.com/grafana/dskit/tenant" + "github.com/oklog/ulid" + "github.com/pkg/errors" + "golang.org/x/exp/slices" + + v1 "github.com/grafana/pyroscope/api/gen/proto/go/adhocprofiles/v1" + "github.com/grafana/pyroscope/pkg/frontend" + "github.com/grafana/pyroscope/pkg/objstore" + "github.com/grafana/pyroscope/pkg/og/structs/flamebearer" + "github.com/grafana/pyroscope/pkg/og/structs/flamebearer/convert" + "github.com/grafana/pyroscope/pkg/validation" +) + +type AdHocProfiles struct { + services.Service + + logger log.Logger + limits frontend.Limits + bucket objstore.Bucket +} + +type AdHocProfile struct { + Name string `json:"name"` + Data string `json:"data"` + UploadedAt time.Time `json:"uploadedAt"` +} + +func NewAdHocProfiles(bucket objstore.Bucket, logger log.Logger, limits frontend.Limits) *AdHocProfiles { + a := &AdHocProfiles{ + logger: logger, + bucket: bucket, + limits: limits, + } + a.Service = services.NewBasicService(nil, a.running, nil) + return a +} + +func (a *AdHocProfiles) running(ctx context.Context) error { + <-ctx.Done() + return nil +} + +func (a *AdHocProfiles) Upload(ctx context.Context, c *connect.Request[v1.AdHocProfilesUploadRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) { + tenantID, err := tenant.TenantID(ctx) + if err != nil { + return nil, connect.NewError(connect.CodeInvalidArgument, err) + } + + adHocProfile := AdHocProfile{ + Name: c.Msg.Name, + Data: c.Msg.Profile, + UploadedAt: time.Now().UTC(), + } + + // TODO: Add per-tenant upload limits (number of files, total size, etc.) + + maxNodes, err := validation.ValidateMaxNodes(a.limits, []string{tenantID}, c.Msg.GetMaxNodes()) + if err != nil { + return nil, errors.Wrapf(err, "could not determine max nodes") + } + + profile, profileTypes, err := parse(&adHocProfile, nil, maxNodes) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse profile") + } + + bucket := a.getBucket(tenantID) + + uid := ulid.MustNew(ulid.Timestamp(adHocProfile.UploadedAt), rand.Reader) + id := strings.Join([]string{uid.String(), adHocProfile.Name}, "-") + + dataToStore, err := json.Marshal(adHocProfile) + if err != nil { + return nil, errors.Wrapf(err, "failed to upload profile") + } + + err = bucket.Upload(ctx, id, bytes.NewReader(dataToStore)) + if err != nil { + return nil, errors.Wrapf(err, "failed to upload profile") + } + + jsonProfile, err := json.Marshal(profile) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse profile") + } + + return connect.NewResponse(&v1.AdHocProfilesGetResponse{ + Id: id, + Name: adHocProfile.Name, + UploadedAt: adHocProfile.UploadedAt.UnixMilli(), + FlamebearerProfile: string(jsonProfile), + ProfileType: profile.Metadata.Name, + ProfileTypes: profileTypes, + }), nil +} + +func (a *AdHocProfiles) Get(ctx context.Context, c *connect.Request[v1.AdHocProfilesGetRequest]) (*connect.Response[v1.AdHocProfilesGetResponse], error) { + tenantID, err := tenant.TenantID(ctx) + if err != nil { + return nil, connect.NewError(connect.CodeInvalidArgument, err) + } + + bucket := a.getBucket(tenantID) + + reader, err := bucket.Get(ctx, c.Msg.GetId()) + if err != nil { + return nil, errors.Wrapf(err, "failed to get profile") + } + + adHocProfileBytes, err := io.ReadAll(reader) + if err != nil { + return nil, err + } + + var adHocProfile AdHocProfile + err = json.Unmarshal(adHocProfileBytes, &adHocProfile) + if err != nil { + return nil, err + } + + maxNodes, err := validation.ValidateMaxNodes(a.limits, []string{tenantID}, c.Msg.GetMaxNodes()) + if err != nil { + return nil, errors.Wrapf(err, "could not determine max nodes") + } + + profile, profileTypes, err := parse(&adHocProfile, c.Msg.ProfileType, maxNodes) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse profile") + } + + jsonProfile, err := json.Marshal(profile) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse profile") + } + + return connect.NewResponse(&v1.AdHocProfilesGetResponse{ + Id: c.Msg.Id, + Name: adHocProfile.Name, + UploadedAt: adHocProfile.UploadedAt.UnixMilli(), + FlamebearerProfile: string(jsonProfile), + ProfileType: profile.Metadata.Name, + ProfileTypes: profileTypes, + }), nil +} + +func (a *AdHocProfiles) List(ctx context.Context, c *connect.Request[v1.AdHocProfilesListRequest]) (*connect.Response[v1.AdHocProfilesListResponse], error) { + bucket, err := a.getBucketFromContext(ctx) + if err != nil { + return nil, err + } + + profiles := make([]*v1.AdHocProfilesProfileMetadata, 0) + err = bucket.Iter(ctx, "", func(s string) error { + separatorIndex := strings.IndexRune(s, '-') + id, err := ulid.Parse(s[0:separatorIndex]) + if err != nil { + level.Warn(a.logger).Log("msg", "cannot parse ad hoc profile", "key", s, "err", err) + return nil + } + name := s[separatorIndex+1:] + profiles = append(profiles, &v1.AdHocProfilesProfileMetadata{ + Id: s, + Name: name, + UploadedAt: int64(id.Time()), + }) + return nil + }) + cmp := func(a, b *v1.AdHocProfilesProfileMetadata) int { + if a.UploadedAt < b.UploadedAt { + return 1 + } + if a.UploadedAt > b.UploadedAt { + return -1 + } + return 0 + } + slices.SortFunc(profiles, cmp) + if err != nil { + return nil, err + } + return connect.NewResponse(&v1.AdHocProfilesListResponse{Profiles: profiles}), nil +} + +func (a *AdHocProfiles) getBucketFromContext(ctx context.Context) (objstore.Bucket, error) { + tenantID, err := tenant.TenantID(ctx) + if err != nil { + return nil, connect.NewError(connect.CodeInvalidArgument, err) + } + return a.getBucket(tenantID), nil +} + +func (a *AdHocProfiles) getBucket(tenantID string) objstore.Bucket { + return objstore.NewPrefixedBucket(a.bucket, tenantID+"/adhoc") +} + +func parse(p *AdHocProfile, profileType *string, maxNodes int64) (fg *flamebearer.FlamebearerProfile, profileTypes []string, err error) { + base64decoded, err := base64.StdEncoding.DecodeString(p.Data) + if err != nil { + return nil, nil, errors.Wrapf(err, "failed to upload profile") + } + + f := convert.ProfileFile{ + Name: p.Name, + TypeData: convert.ProfileFileTypeData{}, + Data: base64decoded, + } + + profiles, err := convert.FlamebearerFromFile(f, int(maxNodes)) + if err != nil { + return nil, nil, err + } + if len(profiles) == 0 { + return nil, nil, errors.Wrapf(err, "no profiles found after parsing") + } + + profileTypes = make([]string, 0) + profileTypeIndex := -1 + for i, p := range profiles { + profileTypes = append(profileTypes, p.Metadata.Name) + if profileType != nil && p.Metadata.Name == *profileType { + profileTypeIndex = i + } + } + + var profile *flamebearer.FlamebearerProfile + if profileTypeIndex >= 0 { + profile = profiles[profileTypeIndex] + } else { + profile = profiles[0] + } + + return profile, profileTypes, nil +} diff --git a/pkg/adhocprofiles/adhocprofiles_test.go b/pkg/adhocprofiles/adhocprofiles_test.go new file mode 100644 index 0000000000..d8f47a8559 --- /dev/null +++ b/pkg/adhocprofiles/adhocprofiles_test.go @@ -0,0 +1,177 @@ +package adhocprofiles + +import ( + "bytes" + "context" + "encoding/base64" + "encoding/json" + "os" + "testing" + + "connectrpc.com/connect" + "github.com/stretchr/testify/require" + thanosobjstore "github.com/thanos-io/objstore" + + v1 "github.com/grafana/pyroscope/api/gen/proto/go/adhocprofiles/v1" + phlareobjstore "github.com/grafana/pyroscope/pkg/objstore" + "github.com/grafana/pyroscope/pkg/tenant" + "github.com/grafana/pyroscope/pkg/util" + "github.com/grafana/pyroscope/pkg/validation" +) + +func TestAdHocProfiles_Get(t *testing.T) { + bucket := phlareobjstore.NewBucket(thanosobjstore.NewInMemBucket()) + rawProfile, err := os.ReadFile("testdata/cpu.pprof") + require.NoError(t, err) + encodedProfile := base64.StdEncoding.EncodeToString(rawProfile) + ahp := &AdHocProfile{ + Name: "cpu.pprof", + Data: encodedProfile, + } + jsonProfile, _ := json.Marshal(ahp) + _ = bucket.Upload(context.Background(), "tenant/adhoc/existing-invalid-json", bytes.NewReader([]byte{1, 2, 3})) + _ = bucket.Upload(context.Background(), "tenant/adhoc/existing-valid-profile", bytes.NewReader(jsonProfile)) + type args struct { + ctx context.Context + c *connect.Request[v1.AdHocProfilesGetRequest] + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "reject requests with missing tenant id", + args: args{ + ctx: context.Background(), + c: nil, + }, + wantErr: true, + }, + { + name: "return error when getting a non existing profile", + args: args{ + ctx: tenant.InjectTenantID(context.Background(), "tenant"), + c: connect.NewRequest(&v1.AdHocProfilesGetRequest{Id: "non-existing-id"}), + }, + wantErr: true, + }, + { + name: "return error when getting an existing invalid profile", + args: args{ + ctx: tenant.InjectTenantID(context.Background(), "tenant"), + c: connect.NewRequest(&v1.AdHocProfilesGetRequest{Id: "existing-invalid-profile"}), + }, + wantErr: true, + }, + { + name: "return data when getting an existing valid profile", + args: args{ + ctx: tenant.InjectTenantID(context.Background(), "tenant"), + c: connect.NewRequest(&v1.AdHocProfilesGetRequest{Id: "existing-valid-profile"}), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &AdHocProfiles{ + logger: util.Logger, + limits: validation.MockLimits{MaxFlameGraphNodesDefaultValue: 8192}, + bucket: bucket, + } + _, err := a.Get(tt.args.ctx, tt.args.c) + if (err != nil) != tt.wantErr { + t.Errorf("Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} + +func TestAdHocProfiles_List(t *testing.T) { + bucket := phlareobjstore.NewBucket(thanosobjstore.NewInMemBucket()) + _ = bucket.Upload(context.Background(), "tenant/adhoc/bad-id-should-be-ignored", bytes.NewReader([]byte{1})) + _ = bucket.Upload(context.Background(), "tenant/adhoc/01HMXV8BF4EH71NBYZNPPVGJ2X-cpu.pprof", bytes.NewReader([]byte{1})) + _ = bucket.Upload(context.Background(), "tenant/adhoc/01HMXRV02963FK36GGRE9N6MPH-heap.pprof", bytes.NewReader([]byte{1})) + a := &AdHocProfiles{ + logger: util.Logger, + bucket: bucket, + } + response, err := a.List(tenant.InjectTenantID(context.Background(), "tenant"), connect.NewRequest(&v1.AdHocProfilesListRequest{})) + require.NoError(t, err) + expected := []*v1.AdHocProfilesProfileMetadata{ + { + Id: "01HMXV8BF4EH71NBYZNPPVGJ2X-cpu.pprof", + Name: "cpu.pprof", + UploadedAt: 1706103680484, + }, + { + Id: "01HMXRV02963FK36GGRE9N6MPH-heap.pprof", + Name: "heap.pprof", + UploadedAt: 1706101145673, + }, + } + require.Equal(t, connect.NewResponse(&v1.AdHocProfilesListResponse{Profiles: expected}), response) +} + +func TestAdHocProfiles_Upload(t *testing.T) { + bucket := phlareobjstore.NewBucket(thanosobjstore.NewInMemBucket()) + rawProfile, err := os.ReadFile("testdata/cpu.pprof") + require.NoError(t, err) + encodedProfile := base64.StdEncoding.EncodeToString(rawProfile) + type args struct { + ctx context.Context + c *connect.Request[v1.AdHocProfilesUploadRequest] + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "reject requests with missing tenant id", + args: args{ + ctx: context.Background(), + c: nil, + }, + wantErr: true, + }, + { + name: "should reject an invalid profile", + args: args{ + ctx: tenant.InjectTenantID(context.Background(), "tenant"), + c: connect.NewRequest(&v1.AdHocProfilesUploadRequest{ + Name: "test", + Profile: "123", + }), + }, + wantErr: true, + }, + { + name: "should store a valid profile", + args: args{ + ctx: tenant.InjectTenantID(context.Background(), "tenant"), + c: connect.NewRequest(&v1.AdHocProfilesUploadRequest{ + Name: "test", + Profile: encodedProfile, + }), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &AdHocProfiles{ + logger: util.Logger, + limits: validation.MockLimits{MaxFlameGraphNodesDefaultValue: 8192}, + bucket: bucket, + } + _, err := a.Upload(tt.args.ctx, tt.args.c) + if (err != nil) != tt.wantErr { + t.Errorf("Upload() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/pkg/adhocprofiles/testdata/cpu.pprof b/pkg/adhocprofiles/testdata/cpu.pprof new file mode 100644 index 0000000000..3048f8f80b Binary files /dev/null and b/pkg/adhocprofiles/testdata/cpu.pprof differ diff --git a/pkg/api/api.go b/pkg/api/api.go index 6809da358a..5386cf7692 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -24,6 +24,7 @@ import ( "github.com/grafana/pyroscope/public" + "github.com/grafana/pyroscope/api/gen/proto/go/adhocprofiles/v1/adhocprofilesv1connect" "github.com/grafana/pyroscope/api/gen/proto/go/ingester/v1/ingesterv1connect" "github.com/grafana/pyroscope/api/gen/proto/go/push/v1/pushv1connect" "github.com/grafana/pyroscope/api/gen/proto/go/querier/v1/querierv1connect" @@ -32,6 +33,7 @@ import ( "github.com/grafana/pyroscope/api/gen/proto/go/storegateway/v1/storegatewayv1connect" "github.com/grafana/pyroscope/api/gen/proto/go/version/v1/versionv1connect" "github.com/grafana/pyroscope/api/openapiv2" + "github.com/grafana/pyroscope/pkg/adhocprofiles" "github.com/grafana/pyroscope/pkg/compactor" "github.com/grafana/pyroscope/pkg/distributor" "github.com/grafana/pyroscope/pkg/frontend" @@ -299,3 +301,7 @@ func (a *API) RegisterAdmin(ad *operations.Admin) { {Desc: "Object Storage Tenants & Blocks", Path: "/ops/object-store/tenants"}, }) } + +func (a *API) RegisterAdHocProfiles(ahp *adhocprofiles.AdHocProfiles) { + adhocprofilesv1connect.RegisterAdHocProfileServiceHandler(a.server.HTTP, ahp, a.grpcAuthMiddleware) +} diff --git a/pkg/og/convert/pprof/decoder.go b/pkg/og/convert/pprof/decoder.go new file mode 100644 index 0000000000..34ce288dc1 --- /dev/null +++ b/pkg/og/convert/pprof/decoder.go @@ -0,0 +1,48 @@ +package pprof + +import ( + "bufio" + "compress/gzip" + "fmt" + "io" + + "github.com/valyala/bytebufferpool" + + "github.com/grafana/pyroscope/pkg/og/storage/tree" +) + +var bufPool = bytebufferpool.Pool{} + +func Decode(r io.Reader, p *tree.Profile) error { + br := bufio.NewReader(r) + header, err := br.Peek(2) + if err != nil { + return fmt.Errorf("failed to read pprof profile header: %w", err) + } + if header[0] == 0x1f && header[1] == 0x8b { + gzipr, err := gzip.NewReader(br) + if err != nil { + return fmt.Errorf("failed to create pprof profile zip reader: %w", err) + } + r = gzipr + defer gzipr.Close() + } else { + r = br + } + buf := bufPool.Get() + defer bufPool.Put(buf) + if _, err = io.Copy(buf, r); err != nil { + return err + } + return p.UnmarshalVT(buf.Bytes()) +} + +func DecodePool(r io.Reader, fn func(*tree.Profile) error) error { + p := tree.ProfileFromVTPool() + defer p.ReturnToVTPool() + p.Reset() + if err := Decode(r, p); err != nil { + return err + } + return fn(p) +} diff --git a/pkg/og/structs/flamebearer/convert/convert.go b/pkg/og/structs/flamebearer/convert/convert.go new file mode 100644 index 0000000000..66ba930713 --- /dev/null +++ b/pkg/og/structs/flamebearer/convert/convert.go @@ -0,0 +1,275 @@ +package convert + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "path" + "reflect" + "strconv" + "strings" + "time" + "unicode" + + "github.com/grafana/pyroscope/pkg/og/agent/spy" + "github.com/grafana/pyroscope/pkg/og/convert/perf" + "github.com/grafana/pyroscope/pkg/og/convert/pprof" + "github.com/grafana/pyroscope/pkg/og/storage/metadata" + "github.com/grafana/pyroscope/pkg/og/storage/tree" + "github.com/grafana/pyroscope/pkg/og/structs/flamebearer" +) + +// ProfileFile represents content to be converted to flamebearer. +type ProfileFile struct { + // Name of the file in which the profile was saved. Optional. + // example: pyroscope.server.cpu-2022-01-23T14:31:43Z.json + Name string + // Type of profile. Optional. + Type ProfileFileType + TypeData ProfileFileTypeData + // Raw profile bytes. Required, min length 2. + Data []byte +} + +type ProfileFileType string + +type ProfileFileTypeData struct { + SpyName string + Units metadata.Units +} + +const ( + ProfileFileTypeJSON ProfileFileType = "json" + ProfileFileTypePprof ProfileFileType = "pprof" + ProfileFileTypeCollapsed ProfileFileType = "collapsed" + ProfileFileTypePerfScript ProfileFileType = "perf_script" +) + +type ConverterFn func(b []byte, name string, maxNodes int) ([]*flamebearer.FlamebearerProfile, error) + +var formatConverters = map[ProfileFileType]ConverterFn{ + ProfileFileTypeJSON: JSONToProfile, + ProfileFileTypePprof: PprofToProfile, + ProfileFileTypeCollapsed: CollapsedToProfile, + ProfileFileTypePerfScript: PerfScriptToProfile, +} + +func FlamebearerFromFile(f ProfileFile, maxNodes int) ([]*flamebearer.FlamebearerProfile, error) { + convertFn, _, err := Converter(f) + if err != nil { + return nil, err + } + return convertFn(f.Data, f.Name, maxNodes) +} + +// Converter returns a ConverterFn that converts to +// FlamebearerProfile and overrides any specified fields. +func Converter(p ProfileFile) (ConverterFn, ProfileFileType, error) { + convertFn, err := converter(p) + if err != nil { + return nil, "", err + } + return func(b []byte, name string, maxNodes int) ([]*flamebearer.FlamebearerProfile, error) { + fbs, err := convertFn(b, name, maxNodes) + if err != nil { + return nil, fmt.Errorf("unable to process the profile. The profile was detected as %q: %w", + converterToFormat(convertFn), err) + } + // Overwrite fields if available + if p.TypeData.SpyName != "" { + for _, fb := range fbs { + fb.Metadata.SpyName = p.TypeData.SpyName + } + } + // Replace the units if provided + if p.TypeData.Units != "" { + for _, fb := range fbs { + if fb.Metadata.Units == "" { + fb.Metadata.Units = p.TypeData.Units + } + } + } + return fbs, nil + }, converterToFormat(convertFn), nil +} + +// Note that converterToFormat works only for converter output, +// Converter wraps the returned function into anonymous one. +func converterToFormat(f ConverterFn) ProfileFileType { + switch reflect.ValueOf(f).Pointer() { + case reflect.ValueOf(JSONToProfile).Pointer(): + return ProfileFileTypeJSON + case reflect.ValueOf(PprofToProfile).Pointer(): + return ProfileFileTypePprof + case reflect.ValueOf(CollapsedToProfile).Pointer(): + return ProfileFileTypeCollapsed + case reflect.ValueOf(PerfScriptToProfile).Pointer(): + return ProfileFileTypePerfScript + } + return "unknown" +} + +// TODO(kolesnikovae): +// +// Consider simpler (but more reliable) logic for format identification +// with fallbacks: from the most strict format to the loosest one, e.g: +// pprof, json, collapsed, perf. +func converter(p ProfileFile) (ConverterFn, error) { + if f, ok := formatConverters[p.Type]; ok { + return f, nil + } + ext := strings.TrimPrefix(path.Ext(p.Name), ".") + if f, ok := formatConverters[ProfileFileType(ext)]; ok { + return f, nil + } + if ext == "txt" { + if perf.IsPerfScript(p.Data) { + return PerfScriptToProfile, nil + } + return CollapsedToProfile, nil + } + if len(p.Data) < 2 { + return nil, errors.New("profile is too short") + } + if p.Data[0] == '{' { + return JSONToProfile, nil + } + if p.Data[0] == '\x1f' && p.Data[1] == '\x8b' { + // gzip magic number, assume pprof + return PprofToProfile, nil + } + // Unclear whether it's uncompressed pprof or collapsed, let's check if all the bytes are printable + // This will be slow for collapsed format, but should be fast enough for pprof, which is the most usual case, + // but we have a reasonable upper bound just in case. + // TODO(abeaumont): This won't work with collapsed format with non-ascii encodings. + for i, b := range p.Data { + if i == 100 { + break + } + if !unicode.IsPrint(rune(b)) && !unicode.IsSpace(rune(b)) { + return PprofToProfile, nil + } + } + if perf.IsPerfScript(p.Data) { + return PerfScriptToProfile, nil + } + return CollapsedToProfile, nil +} + +func JSONToProfile(b []byte, name string, maxNodes int) ([]*flamebearer.FlamebearerProfile, error) { + var profile flamebearer.FlamebearerProfile + if err := json.Unmarshal(b, &profile); err != nil { + return nil, fmt.Errorf("unable to unmarshall JSON: %w", err) + } + if err := profile.Validate(); err != nil { + return nil, fmt.Errorf("invalid profile: %w", err) + } + + t, err := flamebearer.ProfileToTree(profile) + if err != nil { + return nil, fmt.Errorf("could not convert flameabearer to tree: %w", err) + } + + pc := flamebearer.ProfileConfig{ + Tree: t, + Name: profile.Metadata.Name, + MaxNodes: maxNodes, + Metadata: metadata.Metadata{ + SpyName: profile.Metadata.SpyName, + SampleRate: profile.Metadata.SampleRate, + Units: profile.Metadata.Units, + }, + } + + p := flamebearer.NewProfile(pc) + return []*flamebearer.FlamebearerProfile{&p}, nil +} + +func PprofToProfile(b []byte, name string, maxNodes int) ([]*flamebearer.FlamebearerProfile, error) { + var p tree.Profile + if err := pprof.Decode(bytes.NewReader(b), &p); err != nil { + return nil, fmt.Errorf("parsing pprof: %w", err) + } + fbs := make([]*flamebearer.FlamebearerProfile, 0) + for _, stype := range p.SampleTypes() { + sampleRate := uint32(100) + units := metadata.SamplesUnits + if c, ok := tree.DefaultSampleTypeMapping[stype]; ok { + units = c.Units + if c.Sampled && p.Period > 0 { + sampleRate = uint32(time.Second / time.Duration(p.Period)) + } + } + t := tree.New() + p.Get(stype, func(_labels *spy.Labels, name []byte, val int) error { + t.Insert(name, uint64(val)) + return nil + }) + fb := flamebearer.NewProfile(flamebearer.ProfileConfig{ + Tree: t, + Name: stype, + MaxNodes: maxNodes, + Metadata: metadata.Metadata{ + SpyName: "unknown", + SampleRate: sampleRate, + Units: units, + }, + }) + fbs = append(fbs, &fb) + } + if len(fbs) == 0 { + return nil, errors.New("no supported sample type found") + } + return fbs, nil +} + +func CollapsedToProfile(b []byte, name string, maxNodes int) ([]*flamebearer.FlamebearerProfile, error) { + t := tree.New() + for _, line := range bytes.Split(b, []byte("\n")) { + if len(line) == 0 { + continue + } + i := bytes.LastIndexByte(line, ' ') + if i < 0 { + return nil, errors.New("unable to find stacktrace and value separator") + } + value, err := strconv.ParseUint(string(line[i+1:]), 10, 64) + if err != nil { + return nil, fmt.Errorf("unable to parse sample value: %w", err) + } + t.Insert(line[:i], value) + } + fb := flamebearer.NewProfile(flamebearer.ProfileConfig{ + Name: name, + Tree: t, + MaxNodes: maxNodes, + Metadata: metadata.Metadata{ + SpyName: "unknown", + SampleRate: 100, // We don't have this information, use the default + }, + }) + return []*flamebearer.FlamebearerProfile{&fb}, nil +} + +func PerfScriptToProfile(b []byte, name string, maxNodes int) ([]*flamebearer.FlamebearerProfile, error) { + t := tree.New() + p := perf.NewScriptParser(b) + events, err := p.ParseEvents() + if err != nil { + return nil, err + } + for _, e := range events { + t.InsertStack(e, 1) + } + fb := flamebearer.NewProfile(flamebearer.ProfileConfig{ + Name: name, + Tree: t, + MaxNodes: maxNodes, + Metadata: metadata.Metadata{ + SpyName: "unknown", + SampleRate: 100, // We don't have this information, use the default + }, + }) + return []*flamebearer.FlamebearerProfile{&fb}, nil +} diff --git a/pkg/og/structs/flamebearer/convert/convert_suite_test.go b/pkg/og/structs/flamebearer/convert/convert_suite_test.go new file mode 100644 index 0000000000..7d73600270 --- /dev/null +++ b/pkg/og/structs/flamebearer/convert/convert_suite_test.go @@ -0,0 +1,13 @@ +package convert_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestServer(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Flamebearer Convert Suite") +} diff --git a/pkg/og/structs/flamebearer/convert/convert_test.go b/pkg/og/structs/flamebearer/convert/convert_test.go new file mode 100644 index 0000000000..cfe268cf45 --- /dev/null +++ b/pkg/og/structs/flamebearer/convert/convert_test.go @@ -0,0 +1,704 @@ +package convert + +import ( + "io/ioutil" + "reflect" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/grafana/pyroscope/pkg/og/structs/flamebearer" +) + +var _ = Describe("Server", func() { + Describe("Detecting format", func() { + Context("with a valid pprof type", func() { + When("there's only type", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Type: "pprof", + } + }) + It("should return pprof as type is be enough to detect the type", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("there's pprof type and json filename", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.json", + Type: "pprof", + } + }) + It("should return pprof as type takes precedence over filename", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's pprof type and json profile contents", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte(`{"flamebearer":""}`), + Type: "pprof", + } + }) + It("should return pprof as type takes precedence over profile contents", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with no (valid) type and a valid pprof filename", func() { + When("there's pprof filename and json profile contents", func() { + var m ProfileFile + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.pprof", + Data: []byte(`{"flamebearer":""}`), + } + }) + + It("should return pprof as filename takes precedence over profile contents", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("there's pprof filename and an unsupported type", func() { + var m ProfileFile + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.pprof", + Type: "unsupported", + } + }) + + It("should return pprof as unsupported type is ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with no (valid) type and filename, a valid pprof profile", func() { + When("there's a profile with uncompressed pprof content", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte{0x0a, 0x04}, + } + }) + + It("should return pprof", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's a profile with compressed pprof content", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte{0x1f, 0x8b}, + } + }) + + It("should return pprof", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's a profile with compressed pprof content and an unsupported type", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte{0x1f, 0x8b}, + Type: "unsupported", + } + }) + + It("should return pprof as unsupported types are ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's a profile with compressed pprof content and unsupported filename", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.unsupported", + Data: []byte{0x1f, 0x8b}, + } + }) + + It("should return pprof as unsupported filenames are ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PprofToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with a valid json type", func() { + When("there's only type", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Type: "json", + } + }) + It("should return json as type is be enough to detect the type", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("there's json type and pprof filename", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.pprof", + Type: "json", + } + }) + It("should return json as type takes precedence over filename", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's json type and pprof profile contents", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte{0x1f, 0x8b}, + Type: "json", + } + }) + It("should return json as type takes precedence over profile contents", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with no (valid) type and a valid json filename", func() { + When("there's json filename and pprof profile contents", func() { + var m ProfileFile + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.json", + Data: []byte{0x1f, 0x8b}, + } + }) + + It("should return json as filename takes precedence over profile contents", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("there's json filename and an unsupported type", func() { + var m ProfileFile + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.json", + Type: "unsupported", + } + }) + + It("should return json as unsupported type is ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with no (valid) type and filename, a valid json profile", func() { + When("there's a profile with json content", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte(`{"flamebearer":""}`), + } + }) + + It("should return json", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's a profile with json content and an unsupported type", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte(`{"flamebearer":""}`), + Type: "unsupported", + } + }) + + It("should return json as unsupported types are ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's a profile with json content and unsupported filename", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.unsupported", + Data: []byte(`{"flamebearer":""}`), + } + }) + + It("should return json as unsupported filenames are ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(JSONToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with a valid collapsed type", func() { + When("there's only type", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Type: "collapsed", + } + }) + It("should return collapsed as type is be enough to detect the type", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("there's collapsed type and pprof filename", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.pprof", + Type: "collapsed", + } + }) + It("should return collapsed as type takes precedence over filename", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's json type and pprof profile contents", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte{0x1f, 0x8b}, + Type: "collapsed", + } + }) + It("should return collapsed as type takes precedence over profile contents", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with no (valid) type and a valid collapsed filename", func() { + When("there's collapsed filename and pprof profile contents", func() { + var m ProfileFile + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.collapsed", + Data: []byte{0x1f, 0x8b}, + } + }) + + It("should return collapsed as filename takes precedence over profile contents", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("there's collapsed filename and an unsupported type", func() { + var m ProfileFile + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.collapsed", + Type: "unsupported", + } + }) + + It("should return collapsed as unsupported type is ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's collapsed text filename and an unsupported type", func() { + var m ProfileFile + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.collapsed.txt", + Type: "unsupported", + } + }) + + It("should return collapsed as unsupported type is ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with no (valid) type and filename, a valid collapsed profile", func() { + When("there's a profile with collapsed content", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte("fn1 1\nfn2 2"), + } + }) + + It("should return collapsed", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's a profile with collapsed content and an unsupported type", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte("fn1 1\nfn2 2"), + Type: "unsupported", + } + }) + + It("should return collapsed as unsupported types are ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + + When("there's a profile with collapsed content and unsupported filename", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "profile.unsupported", + Data: []byte("fn1 1\nfn2 2"), + } + }) + + It("should return collapsed as unsupported filenames are ignored", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(CollapsedToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("perf script", func() { + When("detect by content", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Data: []byte("java 12688 [002] 6544038.708352: cpu-clock:\n\n"), + } + }) + + It("should return perf_script", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PerfScriptToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("detect by .txt extension and content", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "foo.txt", + Data: []byte("java 12688 [002] 6544038.708352: cpu-clock:\n\n"), + } + }) + + It("should return perf_script", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PerfScriptToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + When("detect by .perf_script extension", func() { + var m ProfileFile + + BeforeEach(func() { + m = ProfileFile{ + Name: "foo.perf_script", + Data: []byte("foo;bar 239"), + } + }) + + It("should return perf_script", func() { + // We want to compare functions, which is not ideal. + expected := reflect.ValueOf(PerfScriptToProfile).Pointer() + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + Expect(reflect.ValueOf(f).Pointer()).To(Equal(expected)) + }) + }) + }) + + Context("with an empty ProfileFile", func() { + var m ProfileFile + It("should return an error", func() { + _, err := converter(m) + Expect(err).ToNot(Succeed()) + }) + }) + }) + + Describe("Calling DiffV1", func() { + Context("with v1 profiles", func() { + var base, diff *flamebearer.FlamebearerProfile + + When("Diff is called with valid and equal base and diff profiles", func() { + BeforeEach(func() { + base = &flamebearer.FlamebearerProfile{ + Version: 1, + FlamebearerProfileV1: flamebearer.FlamebearerProfileV1{ + Metadata: flamebearer.FlamebearerMetadataV1{ + Format: "single", + }, + // Taken from flamebearer test + Flamebearer: flamebearer.FlamebearerV1{ + Names: []string{"total", "a", "c", "b"}, + Levels: [][]int{ + {0, 3, 0, 0}, + {0, 3, 0, 1}, + {0, 1, 1, 3, 0, 2, 2, 2}, + }, + NumTicks: 3, + MaxSelf: 2, + }, + }, + } + + diff = &flamebearer.FlamebearerProfile{ + Version: 1, + FlamebearerProfileV1: flamebearer.FlamebearerProfileV1{ + Metadata: flamebearer.FlamebearerMetadataV1{ + Format: "single", + }, + // Taken from flamebearer test + Flamebearer: flamebearer.FlamebearerV1{ + Names: []string{"total", "a", "c", "b"}, + Levels: [][]int{ + {0, 3, 0, 0}, + {0, 3, 0, 1}, + {0, 1, 1, 3, 0, 2, 2, 2}, + }, + NumTicks: 3, + MaxSelf: 2, + }, + }, + } + }) + + It("returns the diff profile", func() { + fb, err := flamebearer.Diff("name", base, diff, 1024) + Expect(err).To(Succeed()) + Expect(fb.Version).To(Equal(uint(1))) + Expect(fb.Metadata.Name).To(Equal("name")) + Expect(fb.Metadata.Format).To(Equal("double")) + Expect(fb.Flamebearer.Names).To(Equal([]string{"total", "a", "c", "b"})) + Expect(fb.Flamebearer.Levels).To(Equal([][]int{ + {0, 3, 0, 0, 3, 0, 0}, + {0, 3, 0, 0, 3, 0, 1}, + {0, 1, 1, 0, 1, 1, 3, 0, 2, 2, 0, 2, 2, 2}, + })) + Expect(fb.Flamebearer.NumTicks).To(Equal(6)) + Expect(fb.Flamebearer.MaxSelf).To(Equal(2)) + Expect(fb.LeftTicks).To(Equal(uint64(3))) + Expect(fb.RightTicks).To(Equal(uint64(3))) + }) + }) + }) + }) +}) + +var _ = Describe("Convert", func() { + It("converts malformed pprof", func() { + m := ProfileFile{ + Type: "pprof", + Data: readFile("./testdata/cpu-unknown.pb.gz"), + } + + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + + b, err := f(m.Data, "appname", 1024) + Expect(err).To(BeNil()) + Expect(b).ToNot(BeNil()) + }) + + Describe("JSON", func() { + It("prunes tree", func() { + m := ProfileFile{ + Type: "json", + Data: readFile("./testdata/profile.json"), + } + + f, err := converter(m) + Expect(err).To(BeNil()) + Expect(f).ToNot(BeNil()) + + b, err := f(m.Data, "appname", 1) + Expect(err).To(BeNil()) + Expect(b).ToNot(BeNil()) + + // 1 + total + Expect(len(b[0].FlamebearerProfileV1.Flamebearer.Levels)).To(Equal(2)) + }) + }) +}) + +func readFile(path string) []byte { + f, err := ioutil.ReadFile(path) + if err != nil { + panic(err) + } + return f +} diff --git a/pkg/phlare/modules.go b/pkg/phlare/modules.go index d86eee9573..90a61d6350 100644 --- a/pkg/phlare/modules.go +++ b/pkg/phlare/modules.go @@ -31,6 +31,7 @@ import ( "gopkg.in/yaml.v3" statusv1 "github.com/grafana/pyroscope/api/gen/proto/go/status/v1" + "github.com/grafana/pyroscope/pkg/adhocprofiles" apiversion "github.com/grafana/pyroscope/pkg/api/version" "github.com/grafana/pyroscope/pkg/compactor" "github.com/grafana/pyroscope/pkg/distributor" @@ -75,6 +76,7 @@ const ( Compactor string = "compactor" Admin string = "admin" TenantSettings string = "tenant-settings" + AdHocProfiles string = "ad-hoc-profiles" // QueryFrontendTripperware string = "query-frontend-tripperware" // IndexGateway string = "index-gateway" @@ -158,6 +160,17 @@ func (f *Phlare) initTenantSettings() (services.Service, error) { return settings, nil } +func (f *Phlare) initAdHocProfiles() (services.Service, error) { + if f.storageBucket == nil { + level.Warn(f.logger).Log("msg", "no storage bucket configured, ad hoc profiles will not be loaded") + return nil, nil + } + + a := adhocprofiles.NewAdHocProfiles(f.storageBucket, f.logger, f.Overrides) + f.API.RegisterAdHocProfiles(a) + return a, nil +} + func (f *Phlare) initOverrides() (serv services.Service, err error) { f.Overrides, err = validation.NewOverrides(f.Cfg.LimitsConfig, f.TenantLimits) // overrides don't have operational state, nor do they need to do anything more in starting/stopping phase, diff --git a/pkg/phlare/phlare.go b/pkg/phlare/phlare.go index 234764d02b..d8bd68994d 100644 --- a/pkg/phlare/phlare.go +++ b/pkg/phlare/phlare.go @@ -301,10 +301,11 @@ func (f *Phlare) setupModuleManager() error { mm.RegisterModule(Admin, f.initAdmin) mm.RegisterModule(All, nil) mm.RegisterModule(TenantSettings, f.initTenantSettings) + mm.RegisterModule(AdHocProfiles, f.initAdHocProfiles) // Add dependencies deps := map[string][]string{ - All: {Ingester, Distributor, QueryScheduler, QueryFrontend, Querier, StoreGateway, Admin, TenantSettings, Compactor}, + All: {Ingester, Distributor, QueryScheduler, QueryFrontend, Querier, StoreGateway, Admin, TenantSettings, Compactor, AdHocProfiles}, Server: {GRPCGateway}, API: {Server}, @@ -324,6 +325,7 @@ func (f *Phlare) setupModuleManager() error { Admin: {API, Storage}, Version: {API, MemberlistKV}, TenantSettings: {API}, + AdHocProfiles: {API, Overrides}, } for mod, targets := range deps {