Profiling endpoints for mcs (#1)

start and stop profiling endpoints, test includes and many fixes for
policies

deleting duplicated file
This commit is contained in:
Lenin Alevski
2020-04-02 09:57:59 -07:00
committed by GitHub
parent ed3cda28ee
commit 161941d544
25 changed files with 1801 additions and 34 deletions

2
.gitignore vendored
View File

@@ -19,4 +19,6 @@ vendor/
# Ignore executables
target/
mcs-server
mcs
!mcs/
!mcs-server/

View File

@@ -7,11 +7,17 @@ mcs:
swagger-gen:
@echo "Generating swagger server code from yaml"
@swagger generate server -A mcs -f ./swagger.yml -r minio_copyright.txt
@swagger generate server -A mcs -f ./swagger.yml -r NOTICE
build:
@(cd portal-ui; yarn install; make build; cd ..)
@(CGO_ENABLED=0 go build --tags kqueue --ldflags "-s -w" -o mcs ./cmd/mcs-server)
test:
@(go test ./restapi -v)
coverage:
@(go test ./restapi -v -coverprofile=coverage.out && go tool cover -html=coverage.out && open coverage.html)
clean:
@rm -vf mcs

95
models/profiler_type.go Normal file
View File

@@ -0,0 +1,95 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 (
"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
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.Enum(path, location, value, profilerTypeEnum); 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
}

View File

@@ -0,0 +1,83 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 (
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// ProfilingStartRequest profiling start request
//
// swagger:model profilingStartRequest
type ProfilingStartRequest struct {
// type
// Required: true
Type ProfilerType `json:"type"`
}
// Validate validates this profiling start request
func (m *ProfilingStartRequest) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateType(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *ProfilingStartRequest) validateType(formats strfmt.Registry) error {
if err := m.Type.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("type")
}
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *ProfilingStartRequest) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *ProfilingStartRequest) UnmarshalBinary(b []byte) error {
var res ProfilingStartRequest
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,66 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 (
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// StartProfilingItem start profiling item
//
// swagger:model startProfilingItem
type StartProfilingItem struct {
// error
Error string `json:"error,omitempty"`
// node name
NodeName string `json:"nodeName,omitempty"`
// success
Success bool `json:"success,omitempty"`
}
// Validate validates this start profiling item
func (m *StartProfilingItem) Validate(formats strfmt.Registry) error {
return nil
}
// MarshalBinary interface implementation
func (m *StartProfilingItem) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *StartProfilingItem) UnmarshalBinary(b []byte) error {
var res StartProfilingItem
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -0,0 +1,100 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 (
"strconv"
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
)
// StartProfilingList start profiling list
//
// swagger:model startProfilingList
type StartProfilingList struct {
// start results
StartResults []*StartProfilingItem `json:"startResults"`
// number of start results
Total int64 `json:"total,omitempty"`
}
// Validate validates this start profiling list
func (m *StartProfilingList) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateStartResults(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *StartProfilingList) validateStartResults(formats strfmt.Registry) error {
if swag.IsZero(m.StartResults) { // not required
return nil
}
for i := 0; i < len(m.StartResults); i++ {
if swag.IsZero(m.StartResults[i]) { // not required
continue
}
if m.StartResults[i] != nil {
if err := m.StartResults[i].Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("startResults" + "." + strconv.Itoa(i))
}
return err
}
}
}
return nil
}
// MarshalBinary interface implementation
func (m *StartProfilingList) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *StartProfilingList) UnmarshalBinary(b []byte) error {
var res StartProfilingList
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -111,8 +111,7 @@ func parseRawPolicy(rawPolicy *rawPolicy) *models.Policy {
// listPolicies() converts the map[string][]byte returned by client.listPolicies()
// to []*models.Policy by iterating over each key in policyRawMap and
// then using Unmarshal on the raw bytes to create a *models.Policy
func listPolicies(client MinioAdmin) ([]*models.Policy, error) {
ctx := context.Background()
func listPolicies(ctx context.Context, client MinioAdmin) ([]*models.Policy, error) {
policyRawMap, err := client.listPolicies(ctx)
var policies []*models.Policy
if err != nil {
@@ -132,6 +131,7 @@ func listPolicies(client MinioAdmin) ([]*models.Policy, error) {
// getListPoliciesResponse performs listPolicies() and serializes it to the handler's output
func getListPoliciesResponse() (*models.ListPoliciesResponse, error) {
ctx := context.Background()
mAdmin, err := newMAdminClient()
if err != nil {
log.Println("error creating Madmin Client:", err)
@@ -141,7 +141,7 @@ func getListPoliciesResponse() (*models.ListPoliciesResponse, error) {
// defining the client to be used
adminClient := adminClient{client: mAdmin}
policies, err := listPolicies(adminClient)
policies, err := listPolicies(ctx, adminClient)
if err != nil {
log.Println("error listing policies:", err)
return nil, err
@@ -155,8 +155,7 @@ func getListPoliciesResponse() (*models.ListPoliciesResponse, error) {
}
// removePolicy() calls MinIO server to remove a policy based on name.
func removePolicy(client MinioAdmin, name string) error {
ctx := context.Background()
func removePolicy(ctx context.Context, client MinioAdmin, name string) error {
err := client.removePolicy(ctx, name)
if err != nil {
return err
@@ -166,6 +165,7 @@ func removePolicy(client MinioAdmin, name string) error {
// getRemovePolicyResponse() performs removePolicy() and serializes it to the handler's output
func getRemovePolicyResponse(params admin_api.RemovePolicyParams) error {
ctx := context.Background()
if params.Name == "" {
log.Println("error policy name not in request")
return errors.New(500, "error policy name not in request")
@@ -179,7 +179,7 @@ func getRemovePolicyResponse(params admin_api.RemovePolicyParams) error {
// defining the client to be used
adminClient := adminClient{client: mAdmin}
if err := removePolicy(adminClient, params.Name); err != nil {
if err := removePolicy(ctx, adminClient, params.Name); err != nil {
log.Println("error removing policy:", err)
return err
}
@@ -190,12 +190,11 @@ func getRemovePolicyResponse(params admin_api.RemovePolicyParams) error {
// addPolicy() takes name and policy in string format, policy
// policy must be string in json format, in the future this will change
// to a Policy struct{} - https://github.com/minio/minio/issues/9171
func addPolicy(client MinioAdmin, name, policy string) (*models.Policy, error) {
ctx := context.Background()
func addPolicy(ctx context.Context, client MinioAdmin, name, policy string) (*models.Policy, error) {
if err := client.addPolicy(ctx, name, policy); err != nil {
return nil, err
}
policyObject, err := policyInfo(client, name)
policyObject, err := policyInfo(ctx, client, name)
if err != nil {
return nil, err
}
@@ -204,6 +203,7 @@ func addPolicy(client MinioAdmin, name, policy string) (*models.Policy, error) {
// getAddPolicyResponse performs addPolicy() and serializes it to the handler's output
func getAddPolicyResponse(params *models.AddPolicyRequest) (*models.Policy, error) {
ctx := context.Background()
if params == nil {
log.Println("error AddPolicy body not in request")
return nil, errors.New(500, "error AddPolicy body not in request")
@@ -217,7 +217,7 @@ func getAddPolicyResponse(params *models.AddPolicyRequest) (*models.Policy, erro
// create a MinIO Admin Client interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}
policy, err := addPolicy(adminClient, *params.Name, params.Definition)
policy, err := addPolicy(ctx, adminClient, *params.Name, params.Definition)
if err != nil {
log.Println("error adding policy")
return nil, err
@@ -229,8 +229,7 @@ func getAddPolicyResponse(params *models.AddPolicyRequest) (*models.Policy, erro
// policyInfo() takes a policy name, obtains an []byte (represents a string in JSON format)
// from the MinIO server and then convert it to *models.Policy , in the future this will change
// to a Policy struct{} - https://github.com/minio/minio/issues/9171
func policyInfo(client MinioAdmin, name string) (*models.Policy, error) {
ctx := context.Background()
func policyInfo(ctx context.Context, client MinioAdmin, name string) (*models.Policy, error) {
policyRaw, err := client.getPolicy(ctx, name)
if err != nil {
return nil, err
@@ -246,6 +245,7 @@ func policyInfo(client MinioAdmin, name string) (*models.Policy, error) {
// getPolicyInfoResponse performs policyInfo() and serializes it to the handler's output
func getPolicyInfoResponse(params admin_api.PolicyInfoParams) (*models.Policy, error) {
ctx := context.Background()
mAdmin, err := newMAdminClient()
if err != nil {
log.Println("error creating Madmin Client:", err)
@@ -254,7 +254,7 @@ func getPolicyInfoResponse(params admin_api.PolicyInfoParams) (*models.Policy, e
// create a MinIO Admin Client interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}
policy, err := policyInfo(adminClient, params.Name)
policy, err := policyInfo(ctx, adminClient, params.Name)
if err != nil {
log.Println("error getting group info:", err)
return nil, err
@@ -263,12 +263,11 @@ func getPolicyInfoResponse(params admin_api.PolicyInfoParams) (*models.Policy, e
}
// setPolicy() calls MinIO server to assign policy to a group or user.
func setPolicy(client MinioAdmin, name, entityName string, entityType models.PolicyEntity) error {
func setPolicy(ctx context.Context, client MinioAdmin, name, entityName string, entityType models.PolicyEntity) error {
isGroup := false
if entityType == "group" {
isGroup = true
}
ctx := context.Background()
if err := client.setPolicy(ctx, name, entityName, isGroup); err != nil {
return err
}
@@ -277,6 +276,7 @@ func setPolicy(client MinioAdmin, name, entityName string, entityType models.Pol
// getSetPolicyResponse() performs setPolicy() and serializes it to the handler's output
func getSetPolicyResponse(name string, params *models.SetPolicyRequest) error {
ctx := context.Background()
if name == "" {
log.Println("error policy name not in request")
return errors.New(500, "error policy name not in request")
@@ -290,7 +290,7 @@ func getSetPolicyResponse(name string, params *models.SetPolicyRequest) error {
// defining the client to be used
adminClient := adminClient{client: mAdmin}
if err := setPolicy(adminClient, name, *params.EntityName, params.EntityType); err != nil {
if err := setPolicy(ctx, adminClient, name, *params.EntityName, params.EntityType); err != nil {
log.Println("error setting policy:", err)
return err
}

View File

@@ -60,6 +60,7 @@ func (ac adminClientMock) setPolicy(ctx context.Context, policyName, entityName
}
func TestListPolicies(t *testing.T) {
ctx := context.Background()
assert := assert.New(t)
adminClient := adminClientMock{}
mockPoliciesList := map[string][]byte{
@@ -116,7 +117,7 @@ func TestListPolicies(t *testing.T) {
}
// Test-1 : listPolicies() Get response from minio client with three Canned Policies and return the same number on listPolicies()
function := "listPolicies()"
policiesList, err := listPolicies(adminClient)
policiesList, err := listPolicies(ctx, adminClient)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
@@ -147,7 +148,7 @@ func TestListPolicies(t *testing.T) {
minioListPoliciesMock = func() (map[string][]byte, error) {
return nil, errors.New("error")
}
_, err = listPolicies(adminClient)
_, err = listPolicies(ctx, adminClient)
if assert.Error(err) {
assert.Equal("error", err.Error())
}
@@ -158,13 +159,15 @@ func TestListPolicies(t *testing.T) {
}
return malformedData, nil
}
_, err = listPolicies(adminClient)
_, err = listPolicies(ctx, adminClient)
if assert.Error(err) {
assert.NotEmpty(err.Error())
}
}
func TestRemovePolicy(t *testing.T) {
ctx := context.Background()
assert := assert.New(t)
adminClient := adminClientMock{}
// Test-1 : removePolicy() remove an existing policy
@@ -173,19 +176,20 @@ func TestRemovePolicy(t *testing.T) {
return nil
}
function := "removePolicy()"
if err := removePolicy(adminClient, policyToRemove); err != nil {
if err := removePolicy(ctx, adminClient, policyToRemove); err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Test-2 : removePolicy() Return error and see that the error is handled correctly and returned
minioRemovePolicyMock = func(name string) error {
return errors.New("error")
}
if err := removePolicy(adminClient, policyToRemove); assert.Error(err) {
if err := removePolicy(ctx, adminClient, policyToRemove); assert.Error(err) {
assert.Equal("error", err.Error())
}
}
func TestAddPolicy(t *testing.T) {
ctx := context.Background()
assert := assert.New(t)
adminClient := adminClientMock{}
policyName := "new-policy"
@@ -209,7 +213,7 @@ func TestAddPolicy(t *testing.T) {
}
// Test-1 : addPolicy() adds a new policy
function := "addPolicy()"
policy, err := addPolicy(adminClient, policyName, policyDefinition)
policy, err := addPolicy(ctx, adminClient, policyName, policyDefinition)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
@@ -220,7 +224,7 @@ func TestAddPolicy(t *testing.T) {
minioAddPolicyMock = func(name, policy string) error {
return errors.New("error")
}
if _, err := addPolicy(adminClient, policyName, policyDefinition); assert.Error(err) {
if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); assert.Error(err) {
assert.Equal("error", err.Error())
}
// Test-3 : addPolicy() got an error while retrieving policy
@@ -230,19 +234,20 @@ func TestAddPolicy(t *testing.T) {
minioGetPolicyMock = func(name string) (bytes []byte, err error) {
return nil, errors.New("error")
}
if _, err := addPolicy(adminClient, policyName, policyDefinition); assert.Error(err) {
if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); assert.Error(err) {
assert.Equal("error", err.Error())
}
// Test-4 : addPolicy() got an error while parsing policy
minioGetPolicyMock = func(name string) (bytes []byte, err error) {
return []byte("eaeaeaeae"), nil
}
if _, err := addPolicy(adminClient, policyName, policyDefinition); assert.Error(err) {
if _, err := addPolicy(ctx, adminClient, policyName, policyDefinition); assert.Error(err) {
assert.NotEmpty(err.Error())
}
}
func TestSetPolicy(t *testing.T) {
ctx := context.Background()
assert := assert.New(t)
adminClient := adminClientMock{}
policyName := "readOnly"
@@ -253,13 +258,13 @@ func TestSetPolicy(t *testing.T) {
}
// Test-1 : setPolicy() set policy to user
function := "setPolicy()"
err := setPolicy(adminClient, policyName, entityName, entityObject)
err := setPolicy(ctx, adminClient, policyName, entityName, entityObject)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Test-2 : setPolicy() set policy to group
entityObject = models.PolicyEntityGroup
err = setPolicy(adminClient, policyName, entityName, entityObject)
err = setPolicy(ctx, adminClient, policyName, entityName, entityObject)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
@@ -268,7 +273,7 @@ func TestSetPolicy(t *testing.T) {
minioSetPolicyMock = func(policyName, entityName string, isGroup bool) error {
return errors.New("error")
}
if err := setPolicy(adminClient, policyName, entityName, entityObject); assert.Error(err) {
if err := setPolicy(ctx, adminClient, policyName, entityName, entityObject); assert.Error(err) {
assert.Equal("error", err.Error())
}
// Test-4 : setPolicy() set policy to group and get error
@@ -276,7 +281,7 @@ func TestSetPolicy(t *testing.T) {
minioSetPolicyMock = func(policyName, entityName string, isGroup bool) error {
return errors.New("error")
}
if err := setPolicy(adminClient, policyName, entityName, entityObject); assert.Error(err) {
if err := setPolicy(ctx, adminClient, policyName, entityName, entityObject); assert.Error(err) {
assert.Equal("error", err.Error())
}
}

View File

@@ -0,0 +1,118 @@
// This file is part of MinIO Kubernetes Cloud
// Copyright (c) 2020 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 restapi
import (
"bytes"
"context"
"io"
"testing"
"errors"
"github.com/minio/m3/mcs/models"
"github.com/minio/minio/pkg/madmin"
"github.com/stretchr/testify/assert"
)
var minioStartProfiling func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error)
var minioStopProfiling func() (io.ReadCloser, error)
// mock function of startProfiling()
func (ac adminClientMock) startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
return minioStartProfiling(profiler)
}
// mock function of stopProfiling()
func (ac adminClientMock) stopProfiling(ctx context.Context) (io.ReadCloser, error) {
return minioStopProfiling()
}
func TestStartProfiling(t *testing.T) {
ctx := context.Background()
assert := assert.New(t)
adminClient := adminClientMock{}
// Test-1 : startProfiling() Get response from Minio server with one profiling object
// mock function response from startProfiling()
minioStartProfiling = func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
return []madmin.StartProfilingResult{
{
NodeName: "http://127.0.0.1:9000/",
Success: true,
Error: "",
},
{
NodeName: "http://127.0.0.1:9001/",
Success: true,
Error: "",
},
}, nil
}
function := "startProfiling()"
cpuProfiler := models.ProfilerType("cpu")
startProfilingResults, err := startProfiling(ctx, adminClient, cpuProfiler)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
assert.Equal(2, len(startProfilingResults))
//Test-2 : startProfiling() Correctly handles errors returned by Minio
//mock function response from startProfiling()
minioStartProfiling = func(profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
return nil, errors.New("error")
}
_, err = startProfiling(ctx, adminClient, cpuProfiler)
if assert.Error(err) {
assert.Equal("error", err.Error())
}
}
// Implementing fake closingBuffer need it to mock stopProfiling() (io.ReadCloser, error)
type ClosingBuffer struct {
*bytes.Buffer
}
// Implementing a fake Close function for io.ReadCloser
func (cb *ClosingBuffer) Close() error {
return nil
}
func TestStopProfiling(t *testing.T) {
ctx := context.Background()
assert := assert.New(t)
adminClient := adminClientMock{}
// Test-1 : stopProfiling() Get response from Minio server and that response is a readCloser interface
// mock function response from startProfiling()
minioStopProfiling = func() (io.ReadCloser, error) {
return &ClosingBuffer{bytes.NewBufferString("In memory string eaeae")}, nil
}
function := "stopProfiling()"
readCloserInterface, err := stopProfiling(ctx, adminClient)
if err != nil {
t.Errorf("Failed on %s:, error occurred: %s", function, err.Error())
}
// Check return type of stopProfiling is io.ReadCloser by doing a cast
assert.NotPanics(func() { readCloserInterface.(io.ReadCloser).Close() })
// Test-2 : stopProfiling() Correctly handles errors returned by Minio
// mock function response from stopProfiling()
minioStopProfiling = func() (io.ReadCloser, error) {
return nil, errors.New("error")
}
_, err = stopProfiling(ctx, adminClient)
if assert.Error(err) {
assert.Equal("error", err.Error())
}
}

146
restapi/admin_profiling.go Normal file
View File

@@ -0,0 +1,146 @@
// This file is part of MinIO Kubernetes Cloud
// Copyright (c) 2020 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 restapi
import (
"context"
"io"
"log"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/go-openapi/swag"
"github.com/minio/m3/mcs/models"
"github.com/minio/m3/mcs/restapi/operations"
"github.com/minio/m3/mcs/restapi/operations/admin_api"
"github.com/minio/minio/pkg/madmin"
)
func registerProfilingHandler(api *operations.McsAPI) {
// Start Profiling
api.AdminAPIProfilingStartHandler = admin_api.ProfilingStartHandlerFunc(func(params admin_api.ProfilingStartParams, principal interface{}) middleware.Responder {
profilingStartResponse, err := getProfilingStartResponse(params.Body)
if err != nil {
return admin_api.NewProfilingStartDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
return admin_api.NewProfilingStartCreated().WithPayload(profilingStartResponse)
})
// Stop and download profiling data
api.AdminAPIProfilingStopHandler = admin_api.ProfilingStopHandlerFunc(func(params admin_api.ProfilingStopParams, principal interface{}) middleware.Responder {
profilingStopResponse, err := getProfilingStopResponse()
if err != nil {
return admin_api.NewProfilingStopDefault(500).WithPayload(&models.Error{Code: 500, Message: swag.String(err.Error())})
}
// Custom response writer to set the content-disposition header to tell the
// HTTP client the name and extension of the file we are returning
return middleware.ResponderFunc(func(w http.ResponseWriter, _ runtime.Producer) {
w.Header().Set("Content-Type", "application/octet-stream")
w.Header().Set("Content-Disposition", "attachment; filename=profile.zip")
if _, err := io.Copy(w, profilingStopResponse); err != nil {
log.Println(err)
} else {
if err := profilingStopResponse.Close(); err != nil {
log.Println(err)
}
}
})
})
}
// startProfiling() starts the profiling on the Minio server
// Enable 1 of the 7 profiling mechanisms: "cpu","mem","block","mutex","trace","threads","goroutines"
// in the Minio server, returns []*models.StartProfilingItem that contains individual status of this operation
// for each Minio node, ie:
//
// {
// "Success": true,
// "nodeName": "127.0.0.1:9000"
// "error": ""
// }
func startProfiling(ctx context.Context, client MinioAdmin, profilerType models.ProfilerType) ([]*models.StartProfilingItem, error) {
profilingResults, err := client.startProfiling(ctx, madmin.ProfilerType(profilerType))
if err != nil {
return nil, err
}
var items []*models.StartProfilingItem
for _, result := range profilingResults {
items = append(items, &models.StartProfilingItem{
Success: result.Success,
Error: result.Error,
NodeName: result.NodeName,
})
}
return items, nil
}
// getProfilingStartResponse performs startProfiling() and serializes it to the handler's output
func getProfilingStartResponse(params *models.ProfilingStartRequest) (*models.StartProfilingList, error) {
ctx := context.Background()
if params == nil {
log.Println("error profiling type not in body request")
return nil, errors.New(500, "error AddPolicy body not in request")
}
mAdmin, err := newMAdminClient()
if err != nil {
log.Println("error creating Madmin Client:", err)
return nil, err
}
// create a MinIO Admin Client interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}
profilingItems, err := startProfiling(ctx, adminClient, params.Type)
if err != nil {
log.Println("error starting profiling:", err)
return nil, err
}
profilingList := &models.StartProfilingList{
StartResults: profilingItems,
Total: int64(len(profilingItems)),
}
return profilingList, nil
}
// 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)
if err != nil {
return nil, err
}
return profilingData, nil
}
// getProfilingStopResponse() performs setPolicy() and serializes it to the handler's output
func getProfilingStopResponse() (io.ReadCloser, error) {
ctx := context.Background()
mAdmin, err := newMAdminClient()
if err != nil {
log.Println("error creating Madmin Client:", err)
return nil, err
}
// create a MinIO Admin Client interface implementation
// defining the client to be used
adminClient := adminClient{client: mAdmin}
profilingData, err := stopProfiling(ctx, adminClient)
if err != nil {
log.Println("error stopping profiling:", err)
return nil, err
}
return profilingData, nil
}

View File

@@ -20,6 +20,7 @@ import (
"context"
"crypto/tls"
"hash/fnv"
"io"
"net"
"net/http"
"net/url"
@@ -141,7 +142,7 @@ type MinioAdmin interface {
addUser(ctx context.Context, acessKey, SecretKey string) error
listGroups(ctx context.Context) ([]string, error)
updateGroupMembers(ctx context.Context, greq madmin.GroupAddRemove) error
getGroupDescription(ctx context.Context, grouo string) (*madmin.GroupDesc, error)
getGroupDescription(ctx context.Context, group string) (*madmin.GroupDesc, error)
setGroupStatus(ctx context.Context, group string, status madmin.GroupStatus) error
listPolicies(ctx context.Context) (map[string][]byte, error)
getPolicy(ctx context.Context, name string) ([]byte, error)
@@ -153,6 +154,8 @@ type MinioAdmin interface {
setConfigKV(ctx context.Context, kv string) (err error)
serviceRestart(ctx context.Context) error
serverInfo(ctx context.Context) (madmin.InfoMessage, error)
startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error)
stopProfiling(ctx context.Context) (io.ReadCloser, error)
}
// Interface implementation
@@ -243,6 +246,16 @@ func (ac adminClient) serverInfo(ctx context.Context) (madmin.InfoMessage, error
return ac.client.ServerInfo(ctx)
}
// implements madmin.StartProfiling()
func (ac adminClient) startProfiling(ctx context.Context, profiler madmin.ProfilerType) ([]madmin.StartProfilingResult, error) {
return ac.client.StartProfiling(ctx, profiler)
}
// implements madmin.DownloadProfilingData()
func (ac adminClient) stopProfiling(ctx context.Context) (io.ReadCloser, error) {
return ac.client.DownloadProfilingData(ctx)
}
func newMAdminClient() (*madmin.AdminClient, error) {
endpoint := getMinIOServer()
accessKeyID := getAccessKey()

View File

@@ -83,6 +83,8 @@ func configureAPI(api *operations.McsAPI) http.Handler {
registerBucketEventsHandlers(api)
// Register service handlers
registerServiceHandlers(api)
// Register profiling handlers
registerProfilingHandler(api)
api.PreServerShutdown = func() {}

View File

@@ -28,6 +28,7 @@
// - application/json
//
// Produces:
// - application/octet-stream
// - application/json
//
// swagger:meta

View File

@@ -701,6 +701,65 @@ func init() {
}
}
},
"/api/v1/profiling/start": {
"post": {
"tags": [
"AdminAPI"
],
"summary": "Start recording profile data",
"operationId": "ProfilingStart",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/profilingStartRequest"
}
}
],
"responses": {
"201": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/startProfilingList"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/api/v1/profiling/stop": {
"post": {
"produces": [
"application/octet-stream"
],
"tags": [
"AdminAPI"
],
"summary": "Stop and download profile data",
"operationId": "ProfilingStop",
"responses": {
"201": {
"description": "A successful response.",
"schema": {
"type": "file"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/api/v1/service/restart": {
"post": {
"tags": [
@@ -1185,6 +1244,29 @@ func init() {
"principal": {
"type": "string"
},
"profilerType": {
"type": "string",
"enum": [
"cpu",
"mem",
"block",
"mutex",
"trace",
"threads",
"goroutines"
]
},
"profilingStartRequest": {
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"$ref": "#/definitions/profilerType"
}
}
},
"setBucketPolicyRequest": {
"type": "object",
"required": [
@@ -1230,6 +1312,36 @@ func init() {
}
}
},
"startProfilingItem": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"nodeName": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"startProfilingList": {
"type": "object",
"properties": {
"startResults": {
"type": "array",
"items": {
"$ref": "#/definitions/startProfilingItem"
}
},
"total": {
"type": "integer",
"format": "int64",
"title": "number of start results"
}
}
},
"statement": {
"type": "object",
"properties": {
@@ -1970,6 +2082,65 @@ func init() {
}
}
},
"/api/v1/profiling/start": {
"post": {
"tags": [
"AdminAPI"
],
"summary": "Start recording profile data",
"operationId": "ProfilingStart",
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/profilingStartRequest"
}
}
],
"responses": {
"201": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/startProfilingList"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/api/v1/profiling/stop": {
"post": {
"produces": [
"application/octet-stream"
],
"tags": [
"AdminAPI"
],
"summary": "Stop and download profile data",
"operationId": "ProfilingStop",
"responses": {
"201": {
"description": "A successful response.",
"schema": {
"type": "file"
}
},
"default": {
"description": "Generic error response.",
"schema": {
"$ref": "#/definitions/error"
}
}
}
}
},
"/api/v1/service/restart": {
"post": {
"tags": [
@@ -2454,6 +2625,29 @@ func init() {
"principal": {
"type": "string"
},
"profilerType": {
"type": "string",
"enum": [
"cpu",
"mem",
"block",
"mutex",
"trace",
"threads",
"goroutines"
]
},
"profilingStartRequest": {
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"$ref": "#/definitions/profilerType"
}
}
},
"setBucketPolicyRequest": {
"type": "object",
"required": [
@@ -2499,6 +2693,36 @@ func init() {
}
}
},
"startProfilingItem": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"nodeName": {
"type": "string"
},
"success": {
"type": "boolean"
}
}
},
"startProfilingList": {
"type": "object",
"properties": {
"startResults": {
"type": "array",
"items": {
"$ref": "#/definitions/startProfilingItem"
}
},
"total": {
"type": "integer",
"format": "int64",
"title": "number of start results"
}
}
},
"statement": {
"type": "object",
"properties": {

View File

@@ -0,0 +1,88 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// ProfilingStartHandlerFunc turns a function with the right signature into a profiling start handler
type ProfilingStartHandlerFunc func(ProfilingStartParams, interface{}) middleware.Responder
// Handle executing the request and returning a response
func (fn ProfilingStartHandlerFunc) Handle(params ProfilingStartParams, principal interface{}) middleware.Responder {
return fn(params, principal)
}
// ProfilingStartHandler interface for that can handle valid profiling start params
type ProfilingStartHandler interface {
Handle(ProfilingStartParams, interface{}) middleware.Responder
}
// NewProfilingStart creates a new http.Handler for the profiling start operation
func NewProfilingStart(ctx *middleware.Context, handler ProfilingStartHandler) *ProfilingStart {
return &ProfilingStart{Context: ctx, Handler: handler}
}
/*ProfilingStart swagger:route POST /api/v1/profiling/start AdminAPI profilingStart
Start recording profile data
*/
type ProfilingStart struct {
Context *middleware.Context
Handler ProfilingStartHandler
}
func (o *ProfilingStart) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewProfilingStartParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal interface{}
if uprinc != nil {
principal = uprinc
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,94 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime"
"github.com/go-openapi/runtime/middleware"
"github.com/minio/m3/mcs/models"
)
// NewProfilingStartParams creates a new ProfilingStartParams object
// no default values defined in spec.
func NewProfilingStartParams() ProfilingStartParams {
return ProfilingStartParams{}
}
// ProfilingStartParams contains all the bound params for the profiling start operation
// typically these are obtained from a http.Request
//
// swagger:parameters ProfilingStart
type ProfilingStartParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
/*
Required: true
In: body
*/
Body *models.ProfilingStartRequest
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewProfilingStartParams() beforehand.
func (o *ProfilingStartParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if runtime.HasBody(r) {
defer r.Body.Close()
var body models.ProfilingStartRequest
if err := route.Consumer.Consume(r.Body, &body); err != nil {
if err == io.EOF {
res = append(res, errors.Required("body", "body"))
} else {
res = append(res, errors.NewParseError("body", "body", "", err))
}
} else {
// validate body object
if err := body.Validate(route.Formats); err != nil {
res = append(res, err)
}
if len(res) == 0 {
o.Body = &body
}
}
} else {
res = append(res, errors.Required("body", "body"))
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,133 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/m3/mcs/models"
)
// ProfilingStartCreatedCode is the HTTP code returned for type ProfilingStartCreated
const ProfilingStartCreatedCode int = 201
/*ProfilingStartCreated A successful response.
swagger:response profilingStartCreated
*/
type ProfilingStartCreated struct {
/*
In: Body
*/
Payload *models.StartProfilingList `json:"body,omitempty"`
}
// NewProfilingStartCreated creates ProfilingStartCreated with default headers values
func NewProfilingStartCreated() *ProfilingStartCreated {
return &ProfilingStartCreated{}
}
// WithPayload adds the payload to the profiling start created response
func (o *ProfilingStartCreated) WithPayload(payload *models.StartProfilingList) *ProfilingStartCreated {
o.Payload = payload
return o
}
// SetPayload sets the payload to the profiling start created response
func (o *ProfilingStartCreated) SetPayload(payload *models.StartProfilingList) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ProfilingStartCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(201)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}
/*ProfilingStartDefault Generic error response.
swagger:response profilingStartDefault
*/
type ProfilingStartDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewProfilingStartDefault creates ProfilingStartDefault with default headers values
func NewProfilingStartDefault(code int) *ProfilingStartDefault {
if code <= 0 {
code = 500
}
return &ProfilingStartDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the profiling start default response
func (o *ProfilingStartDefault) WithStatusCode(code int) *ProfilingStartDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the profiling start default response
func (o *ProfilingStartDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the profiling start default response
func (o *ProfilingStartDefault) WithPayload(payload *models.Error) *ProfilingStartDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the profiling start default response
func (o *ProfilingStartDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ProfilingStartDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,101 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// ProfilingStartURL generates an URL for the profiling start operation
type ProfilingStartURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ProfilingStartURL) WithBasePath(bp string) *ProfilingStartURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ProfilingStartURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *ProfilingStartURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/api/v1/profiling/start"
_basePath := o._basePath
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *ProfilingStartURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *ProfilingStartURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *ProfilingStartURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on ProfilingStartURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on ProfilingStartURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *ProfilingStartURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -0,0 +1,88 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"net/http"
"github.com/go-openapi/runtime/middleware"
)
// ProfilingStopHandlerFunc turns a function with the right signature into a profiling stop handler
type ProfilingStopHandlerFunc func(ProfilingStopParams, interface{}) middleware.Responder
// Handle executing the request and returning a response
func (fn ProfilingStopHandlerFunc) Handle(params ProfilingStopParams, principal interface{}) middleware.Responder {
return fn(params, principal)
}
// ProfilingStopHandler interface for that can handle valid profiling stop params
type ProfilingStopHandler interface {
Handle(ProfilingStopParams, interface{}) middleware.Responder
}
// NewProfilingStop creates a new http.Handler for the profiling stop operation
func NewProfilingStop(ctx *middleware.Context, handler ProfilingStopHandler) *ProfilingStop {
return &ProfilingStop{Context: ctx, Handler: handler}
}
/*ProfilingStop swagger:route POST /api/v1/profiling/stop AdminAPI profilingStop
Stop and download profile data
*/
type ProfilingStop struct {
Context *middleware.Context
Handler ProfilingStopHandler
}
func (o *ProfilingStop) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
route, rCtx, _ := o.Context.RouteInfo(r)
if rCtx != nil {
r = rCtx
}
var Params = NewProfilingStopParams()
uprinc, aCtx, err := o.Context.Authorize(r, route)
if err != nil {
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
if aCtx != nil {
r = aCtx
}
var principal interface{}
if uprinc != nil {
principal = uprinc
}
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
o.Context.Respond(rw, r, route.Produces, route, err)
return
}
res := o.Handler.Handle(Params, principal) // actually handle the request
o.Context.Respond(rw, r, route.Produces, route, res)
}

View File

@@ -0,0 +1,62 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"net/http"
"github.com/go-openapi/errors"
"github.com/go-openapi/runtime/middleware"
)
// NewProfilingStopParams creates a new ProfilingStopParams object
// no default values defined in spec.
func NewProfilingStopParams() ProfilingStopParams {
return ProfilingStopParams{}
}
// ProfilingStopParams contains all the bound params for the profiling stop operation
// typically these are obtained from a http.Request
//
// swagger:parameters ProfilingStop
type ProfilingStopParams struct {
// HTTP Request Object
HTTPRequest *http.Request `json:"-"`
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
// for simple values it will use straight method calls.
//
// To ensure default values, the struct must have been initialized with NewProfilingStopParams() beforehand.
func (o *ProfilingStopParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
var res []error
o.HTTPRequest = r
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}

View File

@@ -0,0 +1,132 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"io"
"net/http"
"github.com/go-openapi/runtime"
"github.com/minio/m3/mcs/models"
)
// ProfilingStopCreatedCode is the HTTP code returned for type ProfilingStopCreated
const ProfilingStopCreatedCode int = 201
/*ProfilingStopCreated A successful response.
swagger:response profilingStopCreated
*/
type ProfilingStopCreated struct {
/*
In: Body
*/
Payload io.ReadCloser `json:"body,omitempty"`
}
// NewProfilingStopCreated creates ProfilingStopCreated with default headers values
func NewProfilingStopCreated() *ProfilingStopCreated {
return &ProfilingStopCreated{}
}
// WithPayload adds the payload to the profiling stop created response
func (o *ProfilingStopCreated) WithPayload(payload io.ReadCloser) *ProfilingStopCreated {
o.Payload = payload
return o
}
// SetPayload sets the payload to the profiling stop created response
func (o *ProfilingStopCreated) SetPayload(payload io.ReadCloser) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ProfilingStopCreated) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(201)
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
/*ProfilingStopDefault Generic error response.
swagger:response profilingStopDefault
*/
type ProfilingStopDefault struct {
_statusCode int
/*
In: Body
*/
Payload *models.Error `json:"body,omitempty"`
}
// NewProfilingStopDefault creates ProfilingStopDefault with default headers values
func NewProfilingStopDefault(code int) *ProfilingStopDefault {
if code <= 0 {
code = 500
}
return &ProfilingStopDefault{
_statusCode: code,
}
}
// WithStatusCode adds the status to the profiling stop default response
func (o *ProfilingStopDefault) WithStatusCode(code int) *ProfilingStopDefault {
o._statusCode = code
return o
}
// SetStatusCode sets the status to the profiling stop default response
func (o *ProfilingStopDefault) SetStatusCode(code int) {
o._statusCode = code
}
// WithPayload adds the payload to the profiling stop default response
func (o *ProfilingStopDefault) WithPayload(payload *models.Error) *ProfilingStopDefault {
o.Payload = payload
return o
}
// SetPayload sets the payload to the profiling stop default response
func (o *ProfilingStopDefault) SetPayload(payload *models.Error) {
o.Payload = payload
}
// WriteResponse to the client
func (o *ProfilingStopDefault) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
rw.WriteHeader(o._statusCode)
if o.Payload != nil {
payload := o.Payload
if err := producer.Produce(rw, payload); err != nil {
panic(err) // let the recovery middleware deal with this
}
}
}

View File

@@ -0,0 +1,101 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 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 admin_api
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the generate command
import (
"errors"
"net/url"
golangswaggerpaths "path"
)
// ProfilingStopURL generates an URL for the profiling stop operation
type ProfilingStopURL struct {
_basePath string
}
// WithBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ProfilingStopURL) WithBasePath(bp string) *ProfilingStopURL {
o.SetBasePath(bp)
return o
}
// SetBasePath sets the base path for this url builder, only required when it's different from the
// base path specified in the swagger spec.
// When the value of the base path is an empty string
func (o *ProfilingStopURL) SetBasePath(bp string) {
o._basePath = bp
}
// Build a url path and query string
func (o *ProfilingStopURL) Build() (*url.URL, error) {
var _result url.URL
var _path = "/api/v1/profiling/stop"
_basePath := o._basePath
_result.Path = golangswaggerpaths.Join(_basePath, _path)
return &_result, nil
}
// Must is a helper function to panic when the url builder returns an error
func (o *ProfilingStopURL) Must(u *url.URL, err error) *url.URL {
if err != nil {
panic(err)
}
if u == nil {
panic("url can't be nil")
}
return u
}
// String returns the string representation of the path with query string
func (o *ProfilingStopURL) String() string {
return o.Must(o.Build()).String()
}
// BuildFull builds a full url with scheme, host, path and query string
func (o *ProfilingStopURL) BuildFull(scheme, host string) (*url.URL, error) {
if scheme == "" {
return nil, errors.New("scheme is required for a full url on ProfilingStopURL")
}
if host == "" {
return nil, errors.New("host is required for a full url on ProfilingStopURL")
}
base, err := o.Build()
if err != nil {
return nil, err
}
base.Scheme = scheme
base.Host = host
return base, nil
}
// StringFull returns the string representation of a complete url
func (o *ProfilingStopURL) StringFull(scheme, host string) string {
return o.Must(o.BuildFull(scheme, host)).String()
}

View File

@@ -59,6 +59,7 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
JSONConsumer: runtime.JSONConsumer(),
BinProducer: runtime.ByteStreamProducer(),
JSONProducer: runtime.JSONProducer(),
AdminAPIAddGroupHandler: admin_api.AddGroupHandlerFunc(func(params admin_api.AddGroupParams, principal interface{}) middleware.Responder {
@@ -115,6 +116,12 @@ func NewMcsAPI(spec *loads.Document) *McsAPI {
AdminAPIPolicyInfoHandler: admin_api.PolicyInfoHandlerFunc(func(params admin_api.PolicyInfoParams, principal interface{}) middleware.Responder {
return middleware.NotImplemented("operation admin_api.PolicyInfo has not yet been implemented")
}),
AdminAPIProfilingStartHandler: admin_api.ProfilingStartHandlerFunc(func(params admin_api.ProfilingStartParams, principal interface{}) middleware.Responder {
return middleware.NotImplemented("operation admin_api.ProfilingStart has not yet been implemented")
}),
AdminAPIProfilingStopHandler: admin_api.ProfilingStopHandlerFunc(func(params admin_api.ProfilingStopParams, principal interface{}) middleware.Responder {
return middleware.NotImplemented("operation admin_api.ProfilingStop has not yet been implemented")
}),
AdminAPIRemoveGroupHandler: admin_api.RemoveGroupHandlerFunc(func(params admin_api.RemoveGroupParams, principal interface{}) middleware.Responder {
return middleware.NotImplemented("operation admin_api.RemoveGroup has not yet been implemented")
}),
@@ -168,6 +175,9 @@ type McsAPI struct {
// - application/json
JSONConsumer runtime.Consumer
// BinProducer registers a producer for the following mime types:
// - application/octet-stream
BinProducer runtime.Producer
// JSONProducer registers a producer for the following mime types:
// - application/json
JSONProducer runtime.Producer
@@ -215,6 +225,10 @@ type McsAPI struct {
UserAPIMakeBucketHandler user_api.MakeBucketHandler
// AdminAPIPolicyInfoHandler sets the operation handler for the policy info operation
AdminAPIPolicyInfoHandler admin_api.PolicyInfoHandler
// AdminAPIProfilingStartHandler sets the operation handler for the profiling start operation
AdminAPIProfilingStartHandler admin_api.ProfilingStartHandler
// AdminAPIProfilingStopHandler sets the operation handler for the profiling stop operation
AdminAPIProfilingStopHandler admin_api.ProfilingStopHandler
// AdminAPIRemoveGroupHandler sets the operation handler for the remove group operation
AdminAPIRemoveGroupHandler admin_api.RemoveGroupHandler
// AdminAPIRemovePolicyHandler sets the operation handler for the remove policy operation
@@ -289,6 +303,9 @@ func (o *McsAPI) Validate() error {
unregistered = append(unregistered, "JSONConsumer")
}
if o.BinProducer == nil {
unregistered = append(unregistered, "BinProducer")
}
if o.JSONProducer == nil {
unregistered = append(unregistered, "JSONProducer")
}
@@ -351,6 +368,12 @@ func (o *McsAPI) Validate() error {
if o.AdminAPIPolicyInfoHandler == nil {
unregistered = append(unregistered, "admin_api.PolicyInfoHandler")
}
if o.AdminAPIProfilingStartHandler == nil {
unregistered = append(unregistered, "admin_api.ProfilingStartHandler")
}
if o.AdminAPIProfilingStopHandler == nil {
unregistered = append(unregistered, "admin_api.ProfilingStopHandler")
}
if o.AdminAPIRemoveGroupHandler == nil {
unregistered = append(unregistered, "admin_api.RemoveGroupHandler")
}
@@ -423,6 +446,8 @@ func (o *McsAPI) ProducersFor(mediaTypes []string) map[string]runtime.Producer {
result := make(map[string]runtime.Producer, len(mediaTypes))
for _, mt := range mediaTypes {
switch mt {
case "application/octet-stream":
result["application/octet-stream"] = o.BinProducer
case "application/json":
result["application/json"] = o.JSONProducer
}
@@ -537,6 +562,14 @@ func (o *McsAPI) initHandlerCache() {
o.handlers["GET"] = make(map[string]http.Handler)
}
o.handlers["GET"]["/api/v1/policies/{name}"] = admin_api.NewPolicyInfo(o.context, o.AdminAPIPolicyInfoHandler)
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)
}
o.handlers["POST"]["/api/v1/profiling/start"] = admin_api.NewProfilingStart(o.context, o.AdminAPIProfilingStartHandler)
if o.handlers["POST"] == nil {
o.handlers["POST"] = make(map[string]http.Handler)
}
o.handlers["POST"]["/api/v1/profiling/stop"] = admin_api.NewProfilingStop(o.context, o.AdminAPIProfilingStopHandler)
if o.handlers["DELETE"] == nil {
o.handlers["DELETE"] = make(map[string]http.Handler)
}

View File

@@ -69,7 +69,6 @@ func login(mc McCmd, accessKey, secretKey *string) (*string, error) {
// Probe the credentials
cfg, pErr := mc.BuildS3Config(getMinIOServer(), *accessKey, *secretKey, "", "auto")
if pErr != nil {
log.Println(pErr)
return nil, ErrInvalidCredentials
}
// if we made it here, the credentials work, generate a session

View File

@@ -270,7 +270,7 @@ paths:
tags:
- AdminAPI
delete:
summary: Remove group
summary: Remove group
operationId: RemoveGroup
parameters:
- name: name
@@ -536,6 +536,44 @@ paths:
security: []
tags:
- UserAPI
/api/v1/profiling/start:
post:
summary: Start recording profile data
operationId: ProfilingStart
parameters:
- name: body
in: body
required: true
schema:
$ref: '#/definitions/profilingStartRequest'
responses:
201:
description: A successful response.
schema:
$ref: '#/definitions/startProfilingList'
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- AdminAPI
/api/v1/profiling/stop:
post:
summary: Stop and download profile data
operationId: ProfilingStop
produces:
- application/octet-stream
responses:
201:
description: A successful response.
schema:
type: file
default:
description: Generic error response.
schema:
$ref: "#/definitions/error"
tags:
- AdminAPI
definitions:
bucketAccess:
type: string
@@ -819,7 +857,7 @@ definitions:
- access
properties:
access:
$ref: "#/definitions/bucketAccess"
$ref: "#/definitions/bucketAccess"
loginDetails:
type: object
properties:
@@ -846,3 +884,40 @@ definitions:
# Structure that holds the `Bearer {TOKEN}` present on authenticated requests
principal:
type: string
startProfilingItem:
type: object
properties:
nodeName:
type: string
success:
type: boolean
error:
type: string
startProfilingList:
type: object
properties:
total:
type: integer
format: int64
title: number of start results
startResults:
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"