1
0
mirror of https://github.com/minio/mc.git synced 2025-11-12 01:02:26 +03:00

Add admin profiling command (#2557)

Implement profiling command to start & download profiling data of
a standalone or all nodes of a cluster.

```
1. Start CPU profiling
   $ mc admin profiling start --type "cpu" myminio/

2. Download latest profiling data under save under profiling.zip
   $ mc admin profiling stop myminio/
```
This commit is contained in:
Anis Elleuch
2018-10-18 01:06:12 +01:00
committed by kannappanr
parent c707a17f30
commit 0dd32c1b26
7 changed files with 300 additions and 72 deletions

View File

@@ -37,6 +37,7 @@ var adminCmd = cli.Command{
adminCredsCmd,
adminConfigCmd,
adminHealCmd,
adminProfilingCmd,
},
}

View File

@@ -0,0 +1,111 @@
/*
* Minio Client (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"strings"
"github.com/minio/cli"
"github.com/minio/mc/pkg/console"
"github.com/minio/mc/pkg/probe"
"github.com/minio/minio/pkg/madmin"
)
var adminProfilingStartFlags = []cli.Flag{
cli.StringFlag{
Name: "type",
Usage: "Profiler type, possible values are: `cpu`, `mem`, `block`, `mutex` and `trace`",
Value: "mem",
},
}
var adminProfilingStartCmd = cli.Command{
Name: "start",
Usage: "Start recording profiling data",
Action: mainAdminProfilingStart,
Before: setGlobalsFromContext,
Flags: append(adminProfilingStartFlags, globalFlags...),
HideHelpCommand: true,
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} [FLAGS] TARGET
FLAGS:
{{range .VisibleFlags}}{{.}}
{{end}}
EXAMPLES:
1. Start CPU profiling
$ {{.HelpName}} --type cpu myminio/
`,
}
func checkAdminProfilingStartSyntax(ctx *cli.Context) {
// Check flags combinations
if len(ctx.Args()) != 1 {
cli.ShowCommandHelpAndExit(ctx, "start", 1) // last argument is exit code
}
profilerTypes := []madmin.ProfilerType{
madmin.ProfilerCPU,
madmin.ProfilerMEM,
madmin.ProfilerBlock,
madmin.ProfilerMutex,
madmin.ProfilerTrace,
}
// Check if the provided profiler type is known and supported
supportedProfiler := false
profilerType := strings.ToLower(ctx.String("type"))
for _, profiler := range profilerTypes {
if profilerType == string(profiler) {
supportedProfiler = true
break
}
}
if !supportedProfiler {
fatalIf(errDummy(), "Profiler type unrecognized. Possible values are: %v.", profilerTypes)
}
}
// mainAdminProfilingStart - the entry function of profiling command
func mainAdminProfilingStart(ctx *cli.Context) error {
// Check for command syntax
checkAdminProfilingStartSyntax(ctx)
// Get the alias parameter from cli
args := ctx.Args()
aliasedURL := args.Get(0)
profilerType := ctx.String("type")
// Create a new Minio Admin Client
client, err := newAdminClient(aliasedURL)
if err != nil {
fatalIf(err.Trace(aliasedURL), "Cannot initialize admin client.")
return nil
}
// Start profiling
_, cmdErr := client.StartProfiling(madmin.ProfilerType(profilerType))
fatalIf(probe.NewError(cmdErr), "Unable to start profiling.")
console.Infoln("Profiling data successfully started.")
return nil
}

106
cmd/admin-profiling-stop.go Normal file
View File

@@ -0,0 +1,106 @@
/*
* Minio Client (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"io"
"io/ioutil"
"os"
"time"
"github.com/minio/cli"
"github.com/minio/mc/pkg/console"
"github.com/minio/mc/pkg/probe"
)
var adminProfilingStopCmd = cli.Command{
Name: "stop",
Usage: "Stop and download profiling data",
Action: mainAdminProfilingStop,
Before: setGlobalsFromContext,
Flags: globalFlags,
HideHelpCommand: true,
CustomHelpTemplate: `NAME:
{{.HelpName}} - {{.Usage}}
USAGE:
{{.HelpName}} [FLAGS] TARGET
FLAGS:
{{range .VisibleFlags}}{{.}}
{{end}}
EXAMPLES:
2. Download latest profiling data in the current directory
$ {{.HelpName}} myminio/
`,
}
func checkAdminProfilingStopSyntax(ctx *cli.Context) {
if len(ctx.Args()) != 1 {
cli.ShowCommandHelpAndExit(ctx, "stop", 1) // last argument is exit code
}
}
// mainAdminProfilingStop - the entry function of profiling stop command
func mainAdminProfilingStop(ctx *cli.Context) error {
// Check for command syntax
checkAdminProfilingStopSyntax(ctx)
// Get the alias parameter from cli
args := ctx.Args()
aliasedURL := args.Get(0)
// Create a new Minio Admin Client
client, err := newAdminClient(aliasedURL)
if err != nil {
fatalIf(err.Trace(aliasedURL), "Cannot initialize admin client.")
return nil
}
// Create profiling zip file
tmpFile, e := ioutil.TempFile("", "mc-profiling-")
fatalIf(probe.NewError(e), "Unable to download profiling data.")
// Ask for profiling data, which will come compressed with zip format
zippedData, adminErr := client.DownloadProfilingData()
fatalIf(probe.NewError(adminErr), "Unable to download profiling data.")
// Copy zip content to target download file
_, e = io.Copy(tmpFile, zippedData)
fatalIf(probe.NewError(e), "Unable to download profiling data.")
// Close everything
zippedData.Close()
tmpFile.Close()
downloadPath := "profiling.zip"
fi, e := os.Stat(downloadPath)
if e == nil && !fi.IsDir() {
e = os.Rename(downloadPath, downloadPath+"."+time.Now().Format("2006-01-02T15:04:05.999999-07:00"))
fatalIf(probe.NewError(e), "Unable to create a backup of profiling.zip")
} else {
if !os.IsNotExist(e) {
fatal(probe.NewError(e), "Unable to download profiling data.")
}
}
fatalIf(probe.NewError(os.Rename(tmpFile.Name(), downloadPath)), "Unable to download profiling data.")
console.Infof("Profiling data successfully downloaded as %s\n", downloadPath)
return nil
}

40
cmd/admin-profiling.go Normal file
View File

@@ -0,0 +1,40 @@
/*
* Minio Client (C) 2018 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"github.com/minio/cli"
)
var adminProfilingCmd = cli.Command{
Name: "profiling",
Usage: "Generate profiling data for debugging purposes",
Action: mainAdminProfiling,
Before: setGlobalsFromContext,
Flags: globalFlags,
Subcommands: []cli.Command{
adminProfilingStartCmd,
adminProfilingStopCmd,
},
HideHelpCommand: true,
}
// mainAdminProfiling is the handle for "mc admin profiling" command.
func mainAdminProfiling(ctx *cli.Context) error {
cli.ShowCommandHelp(ctx, ctx.Args().First())
return nil
}

View File

@@ -45,6 +45,10 @@ func fatalIf(err *probe.Error, msg string, data ...interface{}) {
if err == nil {
return
}
fatal(err, msg, data...)
}
func fatal(err *probe.Error, msg string, data ...interface{}) {
if globalJSON {
errorMsg := errorMessage{
Message: msg,

View File

@@ -36,12 +36,12 @@ func main() {
```
| Service operations | Info operations | Healing operations | Config operations | IAM operations | Misc |
|:----------------------------|:----------------------------|:--------------------------------------|:--------------------------|:------------------------------------|:------------------------------------|
| [`ServiceStatus`](#ServiceStatus) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`AddUser`](#AddUser) | [`SetAdminCredentials`](#SetAdminCredentials) |
| [`ServiceSendAction`](#ServiceSendAction) | | | [`SetConfig`](#SetConfig) | [`SetUserPolicy`](#SetUserPolicy) | [`StartProfiling`](#StartProfiling) |
| | | | [`GetConfigKeys`](#GetConfigKeys) | [`ListUsers`](#ListUsers) | [`DownloadProfilingData`](#DownloadProfilingData) |
| | | | [`SetConfigKeys`](#SetConfigKeys) | [`AddCannedPolicy`](#AddCannedPolicy) | |
| Service operations | Info operations | Healing operations | Config operations | Misc |
|:----------------------------|:----------------------------|:--------------------------------------|:--------------------------|:------------------------------------|
| [`ServiceStatus`](#ServiceStatus) | [`ServerInfo`](#ServerInfo) | [`Heal`](#Heal) | [`GetConfig`](#GetConfig) | [`SetCredentials`](#SetCredentials) |
| [`ServiceSendAction`](#ServiceSendAction) | | | [`SetConfig`](#SetConfig) | [`StartProfiling`](#StartProfiling) |
| | | | [`GetConfigKeys`](#GetConfigKeys) | [`DownloadProfilingData`](#DownloadProfilingData) |
| | | | [`SetConfigKeys`](#SetConfigKeys) | |
## 1. Constructor
@@ -273,7 +273,7 @@ __Example__
<a name="GetConfig"></a>
### GetConfig() ([]byte, error)
Get current `config.json` of a Minio server.
Get config.json of a minio setup.
__Example__
@@ -295,17 +295,37 @@ __Example__
<a name="SetConfig"></a>
### SetConfig(config io.Reader) error
Set a new `config.json` for a Minio server.
### SetConfig(config io.Reader) (SetConfigResult, error)
Set config.json of a minio setup and restart setup for configuration
change to take effect.
| Param | Type | Description |
|---|---|---|
|`st.Status` | _bool_ | true if set-config succeeded, false otherwise. |
|`st.NodeSummary.Name` | _string_ | Network address of the node. |
|`st.NodeSummary.ErrSet` | _bool_ | Bool representation indicating if an error is encountered with the node.|
|`st.NodeSummary.ErrMsg` | _string_ | String representation of the error (if any) on the node.|
__Example__
``` go
config := bytes.NewReader([]byte(`config.json contents go here`))
if err := madmClnt.SetConfig(config); err != nil {
result, err := madmClnt.SetConfig(config)
if err != nil {
log.Fatalf("failed due to: %v", err)
}
log.Println("SetConfig was successful")
var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false)
enc.SetIndent("", "\t")
err = enc.Encode(result)
if err != nil {
log.Fatalln(err)
}
log.Println("SetConfig: ", string(buf.Bytes()))
```
<a name="GetConfigKeys"></a>
@@ -347,72 +367,18 @@ __Example__
log.Println("New configuration successfully set")
```
## 8. IAM operations
<a name="AddCannedPolicy"></a>
### AddCannedPolicy(policyName string, policy string) error
Create a new canned policy on Minio server.
__Example__
## 8. Misc operations
```
policy := `{"Version": "2012-10-17","Statement": [{"Action": ["s3:GetObject"],"Effect": "Allow","Resource": ["arn:aws:s3:::my-bucketname/*"],"Sid": ""}]}`
if err = madmClnt.AddCannedPolicy("get-only", policy); err != nil {
log.Fatalln(err)
}
```
<a name="AddUser"></a>
### AddUser(user string, secret string) error
Add a new user on a Minio server.
__Example__
``` go
if err = madmClnt.AddUser("newuser", "newstrongpassword"); err != nil {
log.Fatalln(err)
}
```
<a name="SetUserPolicy"></a>
### SetUserPolicy(user string, policyName string) error
Enable a canned policy `get-only` for a given user on Minio server.
__Example__
``` go
if err = madmClnt.SetUserPolicy("newuser", "get-only"); err != nil {
log.Fatalln(err)
}
```
<a name="ListUsers"></a>
### ListUsers() (map[string]UserInfo, error)
Lists all users on Minio server.
__Example__
``` go
users, err := madmClnt.ListUsers();
if err != nil {
log.Fatalln(err)
}
for k, v := range users {
fmt.Printf("User %s Status %s\n", k, v.Status)
}
```
## 9. Misc operations
<a name="SetAdminCredentials"></a>
### SetAdminCredentials() error
<a name="SetCredentials"></a>
### SetCredentials() error
Set new credentials of a Minio setup.
__Example__
``` go
err = madmClnt.SetAdminCredentials("YOUR-NEW-ACCESSKEY", "YOUR-NEW-SECRETKEY")
err = madmClnt.SetCredentials("YOUR-NEW-ACCESSKEY", "YOUR-NEW-SECRETKEY")
if err != nil {
log.Fatalln(err)
}

6
vendor/vendor.json vendored
View File

@@ -134,10 +134,10 @@
"revisionTime": "2018-09-25T09:12:15Z"
},
{
"checksumSHA1": "/5zp4gweRyx0NTrMTEwhCqdMaDA=",
"checksumSHA1": "/OdvNthJAc6DQQ7waRjvzAc7Q3c=",
"path": "github.com/minio/minio/pkg/madmin",
"revision": "79e0add0e50323873b5d30fb41ef70584a9b6f22",
"revisionTime": "2018-10-16T19:47:06Z"
"revision": "aebfceeafb410452d1c03edfa69b7bc5509cc1bc",
"revisionTime": "2018-09-29T08:17:01Z"
},
{
"checksumSHA1": "flg07CqTxM9togozKRQiJugao4s=",