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
|
// type
|
||||||
// Required: true
|
// Required: true
|
||||||
Type *ProfilerType `json:"type"`
|
Type *string `json:"type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate validates this profiling start request
|
// Validate validates this profiling start request
|
||||||
@@ -61,51 +61,11 @@ func (m *ProfilingStartRequest) validateType(formats strfmt.Registry) error {
|
|||||||
return err
|
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
|
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 {
|
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
|
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 Inspect = withSuspense(React.lazy(() => import("./Inspect")));
|
||||||
const Register = withSuspense(React.lazy(() => import("../Support/Register")));
|
const Register = withSuspense(React.lazy(() => import("../Support/Register")));
|
||||||
|
const Profile = withSuspense(React.lazy(() => import("../Support/Profile")));
|
||||||
|
|
||||||
const Tools = () => {
|
const Tools = () => {
|
||||||
return (
|
return (
|
||||||
@@ -34,6 +35,7 @@ const Tools = () => {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route path={IAM_PAGES.TOOLS} exact component={ToolsList} />
|
<Route path={IAM_PAGES.TOOLS} exact component={ToolsList} />
|
||||||
<Route path={IAM_PAGES.REGISTER_SUPPORT} exact component={Register} />
|
<Route path={IAM_PAGES.REGISTER_SUPPORT} exact component={Register} />
|
||||||
|
<Route path={IAM_PAGES.PROFILE} exact component={Profile} />
|
||||||
<Route
|
<Route
|
||||||
path={IAM_PAGES.CALL_HOME}
|
path={IAM_PAGES.CALL_HOME}
|
||||||
exact
|
exact
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
MetricsMenuIcon,
|
MetricsMenuIcon,
|
||||||
MonitoringMenuIcon,
|
MonitoringMenuIcon,
|
||||||
PerformanceMenuIcon,
|
PerformanceMenuIcon,
|
||||||
|
ProfileMenuIcon,
|
||||||
SupportMenuIcon,
|
SupportMenuIcon,
|
||||||
TraceMenuIcon,
|
TraceMenuIcon,
|
||||||
UsersMenuIcon,
|
UsersMenuIcon,
|
||||||
@@ -186,6 +187,13 @@ export const validRoutes = (
|
|||||||
icon: PerformanceMenuIcon,
|
icon: PerformanceMenuIcon,
|
||||||
to: IAM_PAGES.TOOLS_SPEEDTEST,
|
to: IAM_PAGES.TOOLS_SPEEDTEST,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Profile",
|
||||||
|
id: "profile",
|
||||||
|
component: NavLink,
|
||||||
|
icon: ProfileMenuIcon,
|
||||||
|
to: IAM_PAGES.PROFILE,
|
||||||
|
},
|
||||||
|
|
||||||
// {
|
// {
|
||||||
// name: "Call Home",
|
// name: "Call Home",
|
||||||
@@ -201,13 +209,6 @@ export const validRoutes = (
|
|||||||
icon: InspectMenuIcon,
|
icon: InspectMenuIcon,
|
||||||
component: NavLink,
|
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
|
||||||
? childItem.customPermissionFnc()
|
? childItem.customPermissionFnc()
|
||||||
: hasPermission(
|
: hasPermission(
|
||||||
CONSOLE_UI_RESOURCE,
|
CONSOLE_UI_RESOURCE,
|
||||||
IAM_PAGES_PERMISSIONS[childItem.to ?? ""]
|
IAM_PAGES_PERMISSIONS[childItem.to ?? ""]
|
||||||
)) ||
|
)) ||
|
||||||
childItem.forceDisplay) &&
|
childItem.forceDisplay) &&
|
||||||
!childItem.fsHidden
|
!childItem.fsHidden
|
||||||
);
|
);
|
||||||
@@ -329,9 +330,9 @@ export const validRoutes = (
|
|||||||
((item.customPermissionFnc
|
((item.customPermissionFnc
|
||||||
? item.customPermissionFnc()
|
? item.customPermissionFnc()
|
||||||
: hasPermission(
|
: hasPermission(
|
||||||
CONSOLE_UI_RESOURCE,
|
CONSOLE_UI_RESOURCE,
|
||||||
IAM_PAGES_PERMISSIONS[item.to ?? ""]
|
IAM_PAGES_PERMISSIONS[item.to ?? ""]
|
||||||
)) ||
|
)) ||
|
||||||
item.forceDisplay) &&
|
item.forceDisplay) &&
|
||||||
!item.fsHidden;
|
!item.fsHidden;
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
|||||||
// HTTP client the name and extension of the file we are returning
|
// HTTP client the name and extension of the file we are returning
|
||||||
return middleware.ResponderFunc(func(w http.ResponseWriter, _ runtime.Producer) {
|
return middleware.ResponderFunc(func(w http.ResponseWriter, _ runtime.Producer) {
|
||||||
defer profilingStopResponse.Close()
|
defer profilingStopResponse.Close()
|
||||||
|
w.Header().Set("Content-Type", "application/zip")
|
||||||
w.Header().Set("Content-Type", "application/octet-stream")
|
|
||||||
w.Header().Set("Content-Disposition", "attachment; filename=profile.zip")
|
w.Header().Set("Content-Disposition", "attachment; filename=profile.zip")
|
||||||
io.Copy(w, profilingStopResponse)
|
io.Copy(w, profilingStopResponse)
|
||||||
})
|
})
|
||||||
@@ -66,7 +65,7 @@ func registerProfilingHandler(api *operations.ConsoleAPI) {
|
|||||||
// "nodeName": "127.0.0.1:9000"
|
// "nodeName": "127.0.0.1:9000"
|
||||||
// "error": ""
|
// "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))
|
profilingResults, err := client.startProfiling(ctx, madmin.ProfilerType(profilerType))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// stopProfiling() stop the profiling on the Minio server and returns
|
||||||
// the generated Zip file as io.ReadCloser
|
// the generated Zip file as io.ReadCloser
|
||||||
func stopProfiling(ctx context.Context, client MinioAdmin) (io.ReadCloser, error) {
|
func stopProfiling(ctx context.Context, client MinioAdmin) (io.ReadCloser, error) {
|
||||||
profilingData, err := client.stopProfiling(ctx)
|
zippedData, err := client.stopProfiling(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return profilingData, nil
|
return zippedData, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getProfilingStopResponse() performs setPolicy() and serializes it to the handler's output
|
// getProfilingStopResponse() performs setPolicy() and serializes it to the handler's output
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import (
|
|||||||
|
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/minio/console/models"
|
|
||||||
"github.com/minio/madmin-go"
|
"github.com/minio/madmin-go"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
@@ -63,7 +62,7 @@ func TestStartProfiling(t *testing.T) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
function := "startProfiling()"
|
function := "startProfiling()"
|
||||||
cpuProfiler := models.ProfilerType("cpu")
|
cpuProfiler := "cpu"
|
||||||
startProfilingResults, err := startProfiling(ctx, adminClient, cpuProfiler)
|
startProfilingResults, err := startProfiling(ctx, adminClient, cpuProfiler)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
// - multipart/form-data
|
// - multipart/form-data
|
||||||
//
|
//
|
||||||
// Produces:
|
// Produces:
|
||||||
|
// - application/zip
|
||||||
// - application/octet-stream
|
// - application/octet-stream
|
||||||
// - application/json
|
// - application/json
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -2984,7 +2984,7 @@ func init() {
|
|||||||
"/profiling/stop": {
|
"/profiling/stop": {
|
||||||
"post": {
|
"post": {
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/octet-stream"
|
"application/zip"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"AdminAPI"
|
"AdminAPI"
|
||||||
@@ -5519,18 +5519,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profilerType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"cpu",
|
|
||||||
"mem",
|
|
||||||
"block",
|
|
||||||
"mutex",
|
|
||||||
"trace",
|
|
||||||
"threads",
|
|
||||||
"goroutines"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"profilingStartRequest": {
|
"profilingStartRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@@ -5538,7 +5526,7 @@ func init() {
|
|||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"$ref": "#/definitions/profilerType"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -9480,7 +9468,7 @@ func init() {
|
|||||||
"/profiling/stop": {
|
"/profiling/stop": {
|
||||||
"post": {
|
"post": {
|
||||||
"produces": [
|
"produces": [
|
||||||
"application/octet-stream"
|
"application/zip"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"AdminAPI"
|
"AdminAPI"
|
||||||
@@ -12141,18 +12129,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"profilerType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"cpu",
|
|
||||||
"mem",
|
|
||||||
"block",
|
|
||||||
"mutex",
|
|
||||||
"trace",
|
|
||||||
"threads",
|
|
||||||
"goroutines"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"profilingStartRequest": {
|
"profilingStartRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@@ -12160,7 +12136,7 @@ func init() {
|
|||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"$ref": "#/definitions/profilerType"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ package operations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -62,6 +63,9 @@ func NewConsoleAPI(spec *loads.Document) *ConsoleAPI {
|
|||||||
JSONConsumer: runtime.JSONConsumer(),
|
JSONConsumer: runtime.JSONConsumer(),
|
||||||
MultipartformConsumer: runtime.DiscardConsumer,
|
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(),
|
BinProducer: runtime.ByteStreamProducer(),
|
||||||
JSONProducer: runtime.JSONProducer(),
|
JSONProducer: runtime.JSONProducer(),
|
||||||
|
|
||||||
@@ -448,6 +452,9 @@ type ConsoleAPI struct {
|
|||||||
// - multipart/form-data
|
// - multipart/form-data
|
||||||
MultipartformConsumer runtime.Consumer
|
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:
|
// BinProducer registers a producer for the following mime types:
|
||||||
// - application/octet-stream
|
// - application/octet-stream
|
||||||
BinProducer runtime.Producer
|
BinProducer runtime.Producer
|
||||||
@@ -766,6 +773,9 @@ func (o *ConsoleAPI) Validate() error {
|
|||||||
unregistered = append(unregistered, "MultipartformConsumer")
|
unregistered = append(unregistered, "MultipartformConsumer")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.ApplicationZipProducer == nil {
|
||||||
|
unregistered = append(unregistered, "ApplicationZipProducer")
|
||||||
|
}
|
||||||
if o.BinProducer == nil {
|
if o.BinProducer == nil {
|
||||||
unregistered = append(unregistered, "BinProducer")
|
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))
|
result := make(map[string]runtime.Producer, len(mediaTypes))
|
||||||
for _, mt := range mediaTypes {
|
for _, mt := range mediaTypes {
|
||||||
switch mt {
|
switch mt {
|
||||||
|
case "application/zip":
|
||||||
|
result["application/zip"] = o.ApplicationZipProducer
|
||||||
case "application/octet-stream":
|
case "application/octet-stream":
|
||||||
result["application/octet-stream"] = o.BinProducer
|
result["application/octet-stream"] = o.BinProducer
|
||||||
case "application/json":
|
case "application/json":
|
||||||
|
|||||||
@@ -2186,7 +2186,7 @@ paths:
|
|||||||
summary: Stop and download profile data
|
summary: Stop and download profile data
|
||||||
operationId: ProfilingStop
|
operationId: ProfilingStop
|
||||||
produces:
|
produces:
|
||||||
- application/octet-stream
|
- application/zip
|
||||||
responses:
|
responses:
|
||||||
201:
|
201:
|
||||||
description: A successful response.
|
description: A successful response.
|
||||||
@@ -3469,23 +3469,13 @@ definitions:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: "#/definitions/startProfilingItem"
|
$ref: "#/definitions/startProfilingItem"
|
||||||
profilerType:
|
|
||||||
type: string
|
|
||||||
enum:
|
|
||||||
- cpu
|
|
||||||
- mem
|
|
||||||
- block
|
|
||||||
- mutex
|
|
||||||
- trace
|
|
||||||
- threads
|
|
||||||
- goroutines
|
|
||||||
profilingStartRequest:
|
profilingStartRequest:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- type
|
- type
|
||||||
properties:
|
properties:
|
||||||
type:
|
type:
|
||||||
$ref: "#/definitions/profilerType"
|
type: string
|
||||||
sessionResponse:
|
sessionResponse:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
Reference in New Issue
Block a user