Profiling endpoint fixes (#1707)
- Added support to download all profile tests - profile.zip file was corrupted after download - Add suspension warning - Add Checkbox support - Support for running multiple profiling types at the same time - fix profiling test Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
@@ -1,110 +0,0 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2022 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
package models
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// ProfilerType profiler type
|
||||
//
|
||||
// swagger:model profilerType
|
||||
type ProfilerType string
|
||||
|
||||
func NewProfilerType(value ProfilerType) *ProfilerType {
|
||||
return &value
|
||||
}
|
||||
|
||||
// Pointer returns a pointer to a freshly-allocated ProfilerType.
|
||||
func (m ProfilerType) Pointer() *ProfilerType {
|
||||
return &m
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// ProfilerTypeCPU captures enum value "cpu"
|
||||
ProfilerTypeCPU ProfilerType = "cpu"
|
||||
|
||||
// ProfilerTypeMem captures enum value "mem"
|
||||
ProfilerTypeMem ProfilerType = "mem"
|
||||
|
||||
// ProfilerTypeBlock captures enum value "block"
|
||||
ProfilerTypeBlock ProfilerType = "block"
|
||||
|
||||
// ProfilerTypeMutex captures enum value "mutex"
|
||||
ProfilerTypeMutex ProfilerType = "mutex"
|
||||
|
||||
// ProfilerTypeTrace captures enum value "trace"
|
||||
ProfilerTypeTrace ProfilerType = "trace"
|
||||
|
||||
// ProfilerTypeThreads captures enum value "threads"
|
||||
ProfilerTypeThreads ProfilerType = "threads"
|
||||
|
||||
// ProfilerTypeGoroutines captures enum value "goroutines"
|
||||
ProfilerTypeGoroutines ProfilerType = "goroutines"
|
||||
)
|
||||
|
||||
// for schema
|
||||
var profilerTypeEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []ProfilerType
|
||||
if err := json.Unmarshal([]byte(`["cpu","mem","block","mutex","trace","threads","goroutines"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
profilerTypeEnum = append(profilerTypeEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (m ProfilerType) validateProfilerTypeEnum(path, location string, value ProfilerType) error {
|
||||
if err := validate.EnumCase(path, location, value, profilerTypeEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate validates this profiler type
|
||||
func (m ProfilerType) Validate(formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
// value enum
|
||||
if err := m.validateProfilerTypeEnum("", "body", m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validates this profiler type based on context it is used
|
||||
func (m ProfilerType) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
@@ -38,7 +38,7 @@ type ProfilingStartRequest struct {
|
||||
|
||||
// type
|
||||
// Required: true
|
||||
Type *ProfilerType `json:"type"`
|
||||
Type *string `json:"type"`
|
||||
}
|
||||
|
||||
// Validate validates this profiling start request
|
||||
@@ -61,51 +61,11 @@ func (m *ProfilingStartRequest) validateType(formats strfmt.Registry) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validate.Required("type", "body", m.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if m.Type != nil {
|
||||
if err := m.Type.Validate(formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("type")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("type")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ContextValidate validate this profiling start request based on the context it is used
|
||||
// ContextValidate validates this profiling start request based on context it is used
|
||||
func (m *ProfilingStartRequest) ContextValidate(ctx context.Context, formats strfmt.Registry) error {
|
||||
var res []error
|
||||
|
||||
if err := m.contextValidateType(ctx, formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProfilingStartRequest) contextValidateType(ctx context.Context, formats strfmt.Registry) error {
|
||||
|
||||
if m.Type != nil {
|
||||
if err := m.Type.ContextValidate(ctx, formats); err != nil {
|
||||
if ve, ok := err.(*errors.Validation); ok {
|
||||
return ve.ValidateName("type")
|
||||
} else if ce, ok := err.(*errors.CompositeError); ok {
|
||||
return ce.ValidateName("type")
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
199
portal-ui/src/screens/Console/Support/Profile.tsx
Normal file
199
portal-ui/src/screens/Console/Support/Profile.tsx
Normal file
@@ -0,0 +1,199 @@
|
||||
import React, { Fragment, useState } from "react";
|
||||
import { Theme } from "@mui/material/styles";
|
||||
import createStyles from "@mui/styles/createStyles";
|
||||
import withStyles from "@mui/styles/withStyles";
|
||||
import { Button, Grid } from "@mui/material";
|
||||
import PageHeader from "../Common/PageHeader/PageHeader";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
|
||||
import api from "../../../common/api";
|
||||
import { ErrorResponseHandler } from "../../../common/types";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
inlineCheckboxes,
|
||||
} from "../Common/FormComponents/common/styleLibrary";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import WarnIcon from "../../../icons/WarnIcon";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
buttonContainer: {
|
||||
marginTop: 24,
|
||||
textAlign: "right",
|
||||
"& .MuiButton-root": {
|
||||
marginLeft: 8,
|
||||
},
|
||||
},
|
||||
dropdown: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
checkboxLabel: {
|
||||
marginTop: 12,
|
||||
marginRight: 4,
|
||||
fontSize: 16,
|
||||
fontWeight: 500,
|
||||
},
|
||||
checkboxDisabled: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
inlineCheckboxes: {
|
||||
...inlineCheckboxes.inlineCheckboxes,
|
||||
alignItems: "center",
|
||||
|
||||
"@media (max-width: 900px)": {
|
||||
flexFlow: "column",
|
||||
alignItems: "flex-start",
|
||||
},
|
||||
},
|
||||
...actionsTray,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
});
|
||||
|
||||
interface IProfileProps {
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const Profile = ({ classes }: IProfileProps) => {
|
||||
const [profilingStarted, setProfilingStarted] = useState<boolean>(false);
|
||||
const [types, setTypes] = useState<string[]>([
|
||||
"cpu",
|
||||
"mem",
|
||||
"block",
|
||||
"mutex",
|
||||
"trace",
|
||||
"threads",
|
||||
"goroutines",
|
||||
]);
|
||||
|
||||
const typesList = [
|
||||
{ label: "cpu", value: "cpu" },
|
||||
{ label: "mem", value: "mem" },
|
||||
{ label: "block", value: "block" },
|
||||
{ label: "mutex", value: "mutex" },
|
||||
{ label: "trace", value: "trace" },
|
||||
{ label: "threads", value: "threads" },
|
||||
{ label: "goroutines", value: "goroutines" },
|
||||
];
|
||||
|
||||
const onCheckboxClick = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
let newArr: string[] = [];
|
||||
if (types.indexOf(e.target.value) > -1) {
|
||||
newArr = types.filter((type) => type !== e.target.value);
|
||||
} else {
|
||||
newArr = [...types, e.target.value];
|
||||
}
|
||||
setTypes(newArr);
|
||||
};
|
||||
|
||||
const startProfiling = () => {
|
||||
if (!profilingStarted) {
|
||||
const typeString = types.join(",");
|
||||
setProfilingStarted(true);
|
||||
api
|
||||
.invoke("POST", `/api/v1/profiling/start`, {
|
||||
type: typeString,
|
||||
})
|
||||
.then(() => {})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
console.log(err);
|
||||
setProfilingStarted(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const stopProfiling = () => {
|
||||
if (profilingStarted) {
|
||||
const anchor = document.createElement("a");
|
||||
document.body.appendChild(anchor);
|
||||
let path = "/api/v1/profiling/stop";
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("POST", path, true);
|
||||
req.responseType = "blob";
|
||||
req.onreadystatechange = () => {
|
||||
if (req.readyState === 4 && req.status === 200) {
|
||||
let filename = "profile.zip";
|
||||
setProfilingStarted(false);
|
||||
var link = document.createElement("a");
|
||||
link.href = window.URL.createObjectURL(req.response);
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
};
|
||||
req.send();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<PageHeader label="Profile" />
|
||||
<PageLayout>
|
||||
<Grid item xs={12} className={classes.boxy}>
|
||||
<Grid item xs={12} className={classes.dropdown}>
|
||||
<Grid
|
||||
item
|
||||
xs={12}
|
||||
className={`${classes.inlineCheckboxes} ${
|
||||
profilingStarted && classes.checkboxDisabled
|
||||
}`}
|
||||
>
|
||||
<div className={classes.checkboxLabel}>Types to profile:</div>
|
||||
{typesList.map((t) => (
|
||||
<CheckboxWrapper
|
||||
checked={types.indexOf(t.value) > -1}
|
||||
disabled={profilingStarted}
|
||||
key={`checkbox-${t.label}`}
|
||||
id={`checkbox-${t.label}`}
|
||||
label={t.label}
|
||||
name={`checkbox-${t.label}`}
|
||||
onChange={onCheckboxClick}
|
||||
value={t.value}
|
||||
/>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={profilingStarted || types.length < 1}
|
||||
onClick={() => {
|
||||
startProfiling();
|
||||
}}
|
||||
>
|
||||
Start Profiling
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={!profilingStarted}
|
||||
onClick={() => {
|
||||
stopProfiling();
|
||||
}}
|
||||
>
|
||||
Stop Profiling
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
{!profilingStarted && (
|
||||
<Fragment>
|
||||
<br />
|
||||
<HelpBox
|
||||
title={
|
||||
"During the profiling run all production traffic will be suspended."
|
||||
}
|
||||
iconComponent={<WarnIcon />}
|
||||
help={<Fragment />}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
</PageLayout>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(Profile);
|
||||
@@ -27,6 +27,7 @@ import withSuspense from "../Common/Components/withSuspense";
|
||||
|
||||
const Inspect = withSuspense(React.lazy(() => import("./Inspect")));
|
||||
const Register = withSuspense(React.lazy(() => import("../Support/Register")));
|
||||
const Profile = withSuspense(React.lazy(() => import("../Support/Profile")));
|
||||
|
||||
const Tools = () => {
|
||||
return (
|
||||
@@ -34,6 +35,7 @@ const Tools = () => {
|
||||
<Switch>
|
||||
<Route path={IAM_PAGES.TOOLS} exact component={ToolsList} />
|
||||
<Route path={IAM_PAGES.REGISTER_SUPPORT} exact component={Register} />
|
||||
<Route path={IAM_PAGES.PROFILE} exact component={Profile} />
|
||||
<Route
|
||||
path={IAM_PAGES.CALL_HOME}
|
||||
exact
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
MetricsMenuIcon,
|
||||
MonitoringMenuIcon,
|
||||
PerformanceMenuIcon,
|
||||
ProfileMenuIcon,
|
||||
SupportMenuIcon,
|
||||
TraceMenuIcon,
|
||||
UsersMenuIcon,
|
||||
@@ -186,6 +187,13 @@ export const validRoutes = (
|
||||
icon: PerformanceMenuIcon,
|
||||
to: IAM_PAGES.TOOLS_SPEEDTEST,
|
||||
},
|
||||
{
|
||||
name: "Profile",
|
||||
id: "profile",
|
||||
component: NavLink,
|
||||
icon: ProfileMenuIcon,
|
||||
to: IAM_PAGES.PROFILE,
|
||||
},
|
||||
|
||||
// {
|
||||
// name: "Call Home",
|
||||
@@ -201,13 +209,6 @@ export const validRoutes = (
|
||||
icon: InspectMenuIcon,
|
||||
component: NavLink,
|
||||
},
|
||||
// {
|
||||
// name: "Profile",
|
||||
// id: "profile",
|
||||
// component: NavLink,
|
||||
// icon: ProfileMenuIcon,
|
||||
// to: IAM_PAGES.PROFILE,
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -315,9 +316,9 @@ export const validRoutes = (
|
||||
((childItem.customPermissionFnc
|
||||
? childItem.customPermissionFnc()
|
||||
: hasPermission(
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES_PERMISSIONS[childItem.to ?? ""]
|
||||
)) ||
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES_PERMISSIONS[childItem.to ?? ""]
|
||||
)) ||
|
||||
childItem.forceDisplay) &&
|
||||
!childItem.fsHidden
|
||||
);
|
||||
@@ -329,9 +330,9 @@ export const validRoutes = (
|
||||
((item.customPermissionFnc
|
||||
? item.customPermissionFnc()
|
||||
: hasPermission(
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES_PERMISSIONS[item.to ?? ""]
|
||||
)) ||
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_PAGES_PERMISSIONS[item.to ?? ""]
|
||||
)) ||
|
||||
item.forceDisplay) &&
|
||||
!item.fsHidden;
|
||||
return res;
|
||||
|
||||
@@ -48,8 +48,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
||||
// HTTP client the name and extension of the file we are returning
|
||||
return middleware.ResponderFunc(func(w http.ResponseWriter, _ runtime.Producer) {
|
||||
defer profilingStopResponse.Close()
|
||||
|
||||
w.Header().Set("Content-Type", "application/octet-stream")
|
||||
w.Header().Set("Content-Type", "application/zip")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=profile.zip")
|
||||
io.Copy(w, profilingStopResponse)
|
||||
})
|
||||
@@ -66,7 +65,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
||||
// "nodeName": "127.0.0.1:9000"
|
||||
// "error": ""
|
||||
// }
|
||||
func startProfiling(ctx context.Context, client MinioAdmin, profilerType models.ProfilerType) ([]*models.StartProfilingItem, error) {
|
||||
func startProfiling(ctx context.Context, client MinioAdmin, profilerType string) ([]*models.StartProfilingItem, error) {
|
||||
profilingResults, err := client.startProfiling(ctx, madmin.ProfilerType(profilerType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -109,11 +108,11 @@ func getProfilingStartResponse(session *models.Principal, params *models.Profili
|
||||
// stopProfiling() stop the profiling on the Minio server and returns
|
||||
// the generated Zip file as io.ReadCloser
|
||||
func stopProfiling(ctx context.Context, client MinioAdmin) (io.ReadCloser, error) {
|
||||
profilingData, err := client.stopProfiling(ctx)
|
||||
zippedData, err := client.stopProfiling(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return profilingData, nil
|
||||
return zippedData, nil
|
||||
}
|
||||
|
||||
// getProfilingStopResponse() performs setPolicy() and serializes it to the handler's output
|
||||
|
||||
@@ -24,7 +24,6 @@ import (
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/minio/console/models"
|
||||
"github.com/minio/madmin-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -63,7 +62,7 @@ func TestStartProfiling(t *testing.T) {
|
||||
}, nil
|
||||
}
|
||||
function := "startProfiling()"
|
||||
cpuProfiler := models.ProfilerType("cpu")
|
||||
cpuProfiler := "cpu"
|
||||
startProfilingResults, err := startProfiling(ctx, adminClient, cpuProfiler)
|
||||
if err != nil {
|
||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
// - multipart/form-data
|
||||
//
|
||||
// Produces:
|
||||
// - application/zip
|
||||
// - application/octet-stream
|
||||
// - application/json
|
||||
//
|
||||
|
||||
@@ -2984,7 +2984,7 @@ func init() {
|
||||
"/profiling/stop": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/octet-stream"
|
||||
"application/zip"
|
||||
],
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
@@ -5519,18 +5519,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"profilerType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cpu",
|
||||
"mem",
|
||||
"block",
|
||||
"mutex",
|
||||
"trace",
|
||||
"threads",
|
||||
"goroutines"
|
||||
]
|
||||
},
|
||||
"profilingStartRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -5538,7 +5526,7 @@ func init() {
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/profilerType"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -9480,7 +9468,7 @@ func init() {
|
||||
"/profiling/stop": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/octet-stream"
|
||||
"application/zip"
|
||||
],
|
||||
"tags": [
|
||||
"AdminAPI"
|
||||
@@ -12141,18 +12129,6 @@ func init() {
|
||||
}
|
||||
}
|
||||
},
|
||||
"profilerType": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"cpu",
|
||||
"mem",
|
||||
"block",
|
||||
"mutex",
|
||||
"trace",
|
||||
"threads",
|
||||
"goroutines"
|
||||
]
|
||||
},
|
||||
"profilingStartRequest": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -12160,7 +12136,7 @@ func init() {
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"$ref": "#/definitions/profilerType"
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@ package operations
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -62,6 +63,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
||||
JSONConsumer: runtime.JSONConsumer(),
|
||||
MultipartformConsumer: runtime.DiscardConsumer,
|
||||
|
||||
ApplicationZipProducer: runtime.ProducerFunc(func(w io.Writer, data interface{}) error {
|
||||
return errors.NotImplemented("applicationZip producer has not yet been implemented")
|
||||
}),
|
||||
BinProducer: runtime.ByteStreamProducer(),
|
||||
JSONProducer: runtime.JSONProducer(),
|
||||
|
||||
@@ -448,6 +452,9 @@ type ConsoleAPI struct {
|
||||
// - multipart/form-data
|
||||
MultipartformConsumer runtime.Consumer
|
||||
|
||||
// ApplicationZipProducer registers a producer for the following mime types:
|
||||
// - application/zip
|
||||
ApplicationZipProducer runtime.Producer
|
||||
// BinProducer registers a producer for the following mime types:
|
||||
// - application/octet-stream
|
||||
BinProducer runtime.Producer
|
||||
@@ -766,6 +773,9 @@ func (o *ConsoleAPI) Validate() error {
|
||||
unregistered = append(unregistered, "MultipartformConsumer")
|
||||
}
|
||||
|
||||
if o.ApplicationZipProducer == nil {
|
||||
unregistered = append(unregistered, "ApplicationZipProducer")
|
||||
}
|
||||
if o.BinProducer == nil {
|
||||
unregistered = append(unregistered, "BinProducer")
|
||||
}
|
||||
@@ -1177,6 +1187,8 @@ func (o *ConsoleAPI) ProducersFor(mediaTypes []string) map[string]runtime.Produc
|
||||
result := make(map[string]runtime.Producer, len(mediaTypes))
|
||||
for _, mt := range mediaTypes {
|
||||
switch mt {
|
||||
case "application/zip":
|
||||
result["application/zip"] = o.ApplicationZipProducer
|
||||
case "application/octet-stream":
|
||||
result["application/octet-stream"] = o.BinProducer
|
||||
case "application/json":
|
||||
|
||||
@@ -2186,7 +2186,7 @@ paths:
|
||||
summary: Stop and download profile data
|
||||
operationId: ProfilingStop
|
||||
produces:
|
||||
- application/octet-stream
|
||||
- application/zip
|
||||
responses:
|
||||
201:
|
||||
description: A successful response.
|
||||
@@ -3469,23 +3469,13 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/startProfilingItem"
|
||||
profilerType:
|
||||
type: string
|
||||
enum:
|
||||
- cpu
|
||||
- mem
|
||||
- block
|
||||
- mutex
|
||||
- trace
|
||||
- threads
|
||||
- goroutines
|
||||
profilingStartRequest:
|
||||
type: object
|
||||
required:
|
||||
- type
|
||||
properties:
|
||||
type:
|
||||
$ref: "#/definitions/profilerType"
|
||||
type: string
|
||||
sessionResponse:
|
||||
type: object
|
||||
properties:
|
||||
|
||||
Reference in New Issue
Block a user